diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index def57a8dd3..1eda6e3f04 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -784,9 +784,37 @@ module ActionController #:nodoc:
# # placed in "app/views/layouts/special.r(html|xml)"
# render :text => "Hi there!", :layout => "special"
#
- # The :text option can also accept a Proc object, which can be used to manually control the page generation. This should
- # generally be avoided, as it violates the separation between code and content, and because almost everything that can be
- # done with this method can also be done more cleanly using one of the other rendering methods, most notably templates.
+ # === Streaming data and/or controlling the page generation
+ #
+ # The :text option can also accept a Proc object, which can be used to:
+ #
+ # 1. stream on-the-fly generated data to the browser. Note that you should
+ # use the methods provided by ActionController::Steaming instead if you
+ # want to stream a buffer or a file.
+ # 2. manually control the page generation. This should generally be avoided,
+ # as it violates the separation between code and content, and because almost
+ # everything that can be done with this method can also be done more cleanly
+ # using one of the other rendering methods, most notably templates.
+ #
+ # Two arguments are passed to the proc, a response object and an
+ # output object. The response object is equivalent to the return
+ # value of the ActionController::Base#response method, and can be used to
+ # control various things in the HTTP response, such as setting the
+ # Content-Type header. The output object is an writable IO-like
+ # object, so one can call write and flush on it.
+ #
+ # The following example demonstrates how one can stream a large amount of
+ # on-the-fly generated data to the browser:
+ #
+ # # Streams about 180 MB of generated data to the browser.
+ # render :text => proc { |response, output|
+ # 10_000_000.times do |i|
+ # output.write("This is line #{i}\n")
+ # output.flush
+ # end
+ # }
+ #
+ # Another example:
#
# # Renders "Hello from code!"
# render :text => proc { |response, output| output.write("Hello from code!") }
diff --git a/actionpack/lib/action_controller/streaming.rb b/actionpack/lib/action_controller/streaming.rb
index b6a6a2e5db..9f80f48c3d 100644
--- a/actionpack/lib/action_controller/streaming.rb
+++ b/actionpack/lib/action_controller/streaming.rb
@@ -1,5 +1,6 @@
module ActionController #:nodoc:
- # Methods for sending files and streams to the browser instead of rendering.
+ # Methods for sending arbitrary data and for streaming files to the browser,
+ # instead of rendering.
module Streaming
DEFAULT_SEND_FILE_OPTIONS = {
:type => 'application/octet-stream'.freeze,
@@ -103,8 +104,11 @@ module ActionController #:nodoc:
end
end
- # Send binary data to the user as a file download. May set content type, apparent file name,
- # and specify whether to show data inline or download as an attachment.
+ # Sends the given binary data to the browser. This method is similar to
+ # render :text => data, but also allows you to specify whether
+ # the browser should display the response as a file attachment (i.e. in a
+ # download dialog) or as inline data. You may also set the content type,
+ # the apparent file name, and other things.
#
# Options:
# * :filename - suggests a filename for the browser to use.
@@ -127,6 +131,10 @@ module ActionController #:nodoc:
# send_data image.data, :type => image.content_type, :disposition => 'inline'
#
# See +send_file+ for more information on HTTP Content-* headers and caching.
+ #
+ # Tip: if you want to stream large amounts of on-the-fly generated
+ # data to the browser, then use render :text => proc { ... }
+ # instead. See ActionController::Base#render for more information.
def send_data(data, options = {}) #:doc:
logger.info "Sending data #{options[:filename]}" if logger
send_file_headers! options.merge(:length => data.size)
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index 568687e9e0..4fef2b443e 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -646,8 +646,10 @@ module ActionView
# Returns a radio button tag for accessing a specified attribute (identified by +method+) on an object
# assigned to the template (identified by +object+). If the current value of +method+ is +tag_value+ the
- # radio button will be checked. Additional options on the input tag can be passed as a
- # hash with +options+.
+ # radio button will be checked.
+ #
+ # To force the radio button to be checked pass :checked => true in the
+ # +options+ hash. You may pass HTML options there as well.
#
# ==== Examples
# # Let's say that @post.category returns "rails":
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 05ce8ff0b5..e2dc883b1b 100755
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -133,41 +133,40 @@ module ActiveRecord
# | | belongs_to |
# generated methods | belongs_to | :polymorphic | has_one
# ----------------------------------+------------+--------------+---------
- # #other | X | X | X
- # #other=(other) | X | X | X
- # #build_other(attributes={}) | X | | X
- # #create_other(attributes={}) | X | | X
- # #other.create!(attributes={}) | | | X
- # #other.nil? | X | X |
+ # other | X | X | X
+ # other=(other) | X | X | X
+ # build_other(attributes={}) | X | | X
+ # create_other(attributes={}) | X | | X
+ # other.create!(attributes={}) | | | X
#
# ===Collection associations (one-to-many / many-to-many)
# | | | has_many
# generated methods | habtm | has_many | :through
# ----------------------------------+-------+----------+----------
- # #others | X | X | X
- # #others=(other,other,...) | X | X | X
- # #other_ids | X | X | X
- # #other_ids=(id,id,...) | X | X | X
- # #others<< | X | X | X
- # #others.push | X | X | X
- # #others.concat | X | X | X
- # #others.build(attributes={}) | X | X | X
- # #others.create(attributes={}) | X | X | X
- # #others.create!(attributes={}) | X | X | X
- # #others.size | X | X | X
- # #others.length | X | X | X
- # #others.count | X | X | X
- # #others.sum(args*,&block) | X | X | X
- # #others.empty? | X | X | X
- # #others.clear | X | X | X
- # #others.delete(other,other,...) | X | X | X
- # #others.delete_all | X | X |
- # #others.destroy_all | X | X | X
- # #others.find(*args) | X | X | X
- # #others.find_first | X | |
- # #others.exists? | X | X | X
- # #others.uniq | X | X | X
- # #others.reset | X | X | X
+ # others | X | X | X
+ # others=(other,other,...) | X | X | X
+ # other_ids | X | X | X
+ # other_ids=(id,id,...) | X | X | X
+ # others<< | X | X | X
+ # others.push | X | X | X
+ # others.concat | X | X | X
+ # others.build(attributes={}) | X | X | X
+ # others.create(attributes={}) | X | X | X
+ # others.create!(attributes={}) | X | X | X
+ # others.size | X | X | X
+ # others.length | X | X | X
+ # others.count | X | X | X
+ # others.sum(args*,&block) | X | X | X
+ # others.empty? | X | X | X
+ # others.clear | X | X | X
+ # others.delete(other,other,...) | X | X | X
+ # others.delete_all | X | X |
+ # others.destroy_all | X | X | X
+ # others.find(*args) | X | X | X
+ # others.find_first | X | |
+ # others.exists? | X | X | X
+ # others.uniq | X | X | X
+ # others.reset | X | X | X
#
# == Cardinality and associations
#
@@ -813,8 +812,6 @@ module ActiveRecord
# [association=(associate)]
# Assigns the associate object, extracts the primary key, sets it as the foreign key,
# and saves the associate object.
- # [association.nil?]
- # Returns +true+ if there is no associated object.
# [build_association(attributes = {})]
# Returns a new object of the associated type that has been instantiated
# with +attributes+ and linked to this object through a foreign key, but has not
@@ -833,7 +830,6 @@ module ActiveRecord
# An Account class declares has_one :beneficiary, which will add:
# * Account#beneficiary (similar to Beneficiary.find(:first, :conditions => "account_id = #{id}"))
# * Account#beneficiary=(beneficiary) (similar to beneficiary.account_id = account.id; beneficiary.save)
- # * Account#beneficiary.nil?
# * Account#build_beneficiary (similar to Beneficiary.new("account_id" => id))
# * Account#create_beneficiary (similar to b = Beneficiary.new("account_id" => id); b.save; b)
#
@@ -934,8 +930,6 @@ module ActiveRecord
# Returns the associated object. +nil+ is returned if none is found.
# [association=(associate)]
# Assigns the associate object, extracts the primary key, and sets it as the foreign key.
- # [association.nil?]
- # Returns +true+ if there is no associated object.
# [build_association(attributes = {})]
# Returns a new object of the associated type that has been instantiated
# with +attributes+ and linked to this object through a foreign key, but has not yet been saved.
@@ -953,7 +947,6 @@ module ActiveRecord
# * Post#author (similar to Author.find(author_id))
# * Post#author=(author) (similar to post.author_id = author.id)
# * Post#author? (similar to post.author == some_author)
- # * Post#author.nil?
# * Post#build_author (similar to post.author = Author.new)
# * Post#create_author (similar to post.author = Author.new; post.author.save; post.author)
# The declaration can also include an options hash to specialize the behavior of the association.
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 982342e509..55ab1facf2 100755
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -886,7 +886,8 @@ module ActiveRecord #:nodoc:
# Deletes the records matching +conditions+ without instantiating the records first, and hence not
# calling the +destroy+ method nor invoking callbacks. This is a single SQL DELETE statement that
# goes straight to the database, much more efficient than +destroy_all+. Be careful with relations
- # though, in particular :dependent rules defined on associations are not honored.
+ # though, in particular :dependent rules defined on associations are not honored. Returns
+ # the number of rows affected.
#
# ==== Parameters
#
diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb
index 88958f4583..e375037b5b 100644
--- a/activerecord/lib/active_record/callbacks.rb
+++ b/activerecord/lib/active_record/callbacks.rb
@@ -104,16 +104,12 @@ module ActiveRecord
# The callback objects have methods named after the callback called with the record as the only parameter, such as:
#
# class BankAccount < ActiveRecord::Base
- # before_save EncryptionWrapper.new("credit_card_number")
- # after_save EncryptionWrapper.new("credit_card_number")
- # after_initialize EncryptionWrapper.new("credit_card_number")
+ # before_save EncryptionWrapper.new
+ # after_save EncryptionWrapper.new
+ # after_initialize EncryptionWrapper.new
# end
#
# class EncryptionWrapper
- # def initialize(attribute)
- # @attribute = attribute
- # end
- #
# def before_save(record)
# record.credit_card_number = encrypt(record.credit_card_number)
# end
@@ -135,7 +131,39 @@ module ActiveRecord
# end
#
# So you specify the object you want messaged on a given callback. When that callback is triggered, the object has
- # a method by the name of the callback messaged.
+ # a method by the name of the callback messaged. You can make these callbacks more flexible by passing in other
+ # initialization data such as the name of the attribute to work with:
+ #
+ # class BankAccount < ActiveRecord::Base
+ # before_save EncryptionWrapper.new("credit_card_number")
+ # after_save EncryptionWrapper.new("credit_card_number")
+ # after_initialize EncryptionWrapper.new("credit_card_number")
+ # end
+ #
+ # class EncryptionWrapper
+ # def initialize(attribute)
+ # @attribute = attribute
+ # end
+ #
+ # def before_save(record)
+ # record.send("#{@attribute}=", encrypt(record.send("#{@attribute}")))
+ # end
+ #
+ # def after_save(record)
+ # record.send("#{@attribute}=", decrypt(record.send("#{@attribute}")))
+ # end
+ #
+ # alias_method :after_find, :after_save
+ #
+ # private
+ # def encrypt(value)
+ # # Secrecy is committed
+ # end
+ #
+ # def decrypt(value)
+ # # Secrecy is unveiled
+ # end
+ # end
#
# The callback macros usually accept a symbol for the method they're supposed to run, but you can also pass a "method string",
# which will then be evaluated within the binding of the callback. Example:
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index 0131d9fac5..c6501113bf 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -21,13 +21,17 @@ else
end
end
-# Fixtures are a way of organizing data that you want to test against; in short, sample data. They come in 3 flavors:
+# Fixtures are a way of organizing data that you want to test against; in short, sample data.
+#
+# = Fixture formats
+#
+# Fixtures come in 3 flavors:
#
# 1. YAML fixtures
# 2. CSV fixtures
# 3. Single-file fixtures
#
-# = YAML fixtures
+# == YAML fixtures
#
# This type of fixture is in YAML format and the preferred default. YAML is a file format which describes data structures
# in a non-verbose, human-readable format. It ships with Ruby 1.8.1+.
@@ -65,9 +69,9 @@ end
# parent_id: 1
# title: Child
#
-# = CSV fixtures
+# == CSV fixtures
#
-# Fixtures can also be kept in the Comma Separated Value format. Akin to YAML fixtures, CSV fixtures are stored
+# Fixtures can also be kept in the Comma Separated Value (CSV) format. Akin to YAML fixtures, CSV fixtures are stored
# in a single file, but instead end with the .csv file extension
# (Rails example: /test/fixtures/web_sites.csv).
#
@@ -90,7 +94,7 @@ end
# Most databases and spreadsheets support exporting to CSV format, so this is a great format for you to choose if you
# have existing data somewhere already.
#
-# = Single-file fixtures
+# == Single-file fixtures
#
# This type of fixture was the original format for Active Record that has since been deprecated in favor of the YAML and CSV formats.
# Fixtures for this format are created by placing text files in a sub-directory (with the name of the model) to the directory
@@ -113,66 +117,54 @@ end
# name => Ruby on Rails
# url => http://www.rubyonrails.org
#
-# = Using Fixtures
+# = Using fixtures in testcases
#
# Since fixtures are a testing construct, we use them in our unit and functional tests. There are two ways to use the
# fixtures, but first let's take a look at a sample unit test:
#
-# require 'web_site'
+# require 'test_helper'
#
# class WebSiteTest < ActiveSupport::TestCase
-# def test_web_site_count
+# test "web_site_count" do
# assert_equal 2, WebSite.count
# end
# end
#
-# As it stands, unless we pre-load the web_site table in our database with two records, this test will fail. Here's the
-# easiest way to add fixtures to the database:
-#
-# ...
-# class WebSiteTest < ActiveSupport::TestCase
-# fixtures :web_sites # add more by separating the symbols with commas
-# ...
-#
-# By adding a "fixtures" method to the test case and passing it a list of symbols (only one is shown here though), we trigger
-# the testing environment to automatically load the appropriate fixtures into the database before each test.
+# By default, the test_helper module will load all of your fixtures into your test database, so this test will succeed.
+# The testing environment will automatically load the all fixtures into the database before each test.
# To ensure consistent data, the environment deletes the fixtures before running the load.
#
-# In addition to being available in the database, the fixtures are also loaded into a hash stored in an instance variable
-# of the test case. It is named after the symbol... so, in our example, there would be a hash available called
-# @web_sites. This is where the "fixture name" comes into play.
+# In addition to being available in the database, the fixture's data may also be accessed by
+# using a special dynamic method, which has the same name as the model, and accepts the
+# name of the fixture to instantiate:
#
-# On top of that, each record is automatically "found" (using Model.find(id)) and placed in the instance variable of its name.
-# So for the YAML fixtures, we'd get @rubyonrails and @google, which could be interrogated using regular Active Record semantics:
-#
-# # test if the object created from the fixture data has the same attributes as the data itself
-# def test_find
-# assert_equal @web_sites["rubyonrails"]["name"], @rubyonrails.name
-# end
-#
-# As seen above, the data hash created from the YAML fixtures would have @web_sites["rubyonrails"]["url"] return
-# "http://www.rubyonrails.org" and @web_sites["google"]["name"] would return "Google". The same fixtures, but loaded
-# from a CSV fixture file, would be accessible via @web_sites["web_site_1"]["name"] == "Ruby on Rails" and have the individual
-# fixtures available as instance variables @web_site_1 and @web_site_2.
-#
-# If you do not wish to use instantiated fixtures (usually for performance reasons) there are two options.
-#
-# - to completely disable instantiated fixtures:
-# self.use_instantiated_fixtures = false
-#
-# - to keep the fixture instance (@web_sites) available, but do not automatically 'find' each instance:
-# self.use_instantiated_fixtures = :no_instances
-#
-# Even if auto-instantiated fixtures are disabled, you can still access them
-# by name via special dynamic methods. Each method has the same name as the
-# model, and accepts the name of the fixture to instantiate:
-#
-# fixtures :web_sites
-#
-# def test_find
+# test "find" do
# assert_equal "Ruby on Rails", web_sites(:rubyonrails).name
# end
#
+# Alternatively, you may enable auto-instantiation of the fixture data. For instance, take the following tests:
+#
+# test "find_alt_method_1" do
+# assert_equal "Ruby on Rails", @web_sites['rubyonrails']['name']
+# end
+#
+# test "find_alt_method_2" do
+# assert_equal "Ruby on Rails", @rubyonrails.news
+# end
+#
+# In order to use these methods to access fixtured data within your testcases, you must specify one of the
+# following in your ActiveSupport::TestCase-derived class:
+#
+# - to fully enable instantiated fixtures (enable alternate methods #1 and #2 above)
+# self.use_instantiated_fixtures = true
+#
+# - create only the hash for the fixtures, do not 'find' each instance (enable alternate method #1 only)
+# self.use_instantiated_fixtures = :no_instances
+#
+# Using either of these alternate methods incurs a performance hit, as the fixtured data must be fully
+# traversed in the database to create the fixture hash and/or instance variables. This is expensive for
+# large sets of fixtured data.
+#
# = Dynamic fixtures with ERb
#
# Some times you don't care about the content of the fixtures as much as you care about the volume. In these cases, you can
@@ -194,21 +186,17 @@ end
# = Transactional fixtures
#
# TestCases can use begin+rollback to isolate their changes to the database instead of having to delete+insert for every test case.
-# They can also turn off auto-instantiation of fixture data since the feature is costly and often unused.
#
# class FooTest < ActiveSupport::TestCase
# self.use_transactional_fixtures = true
-# self.use_instantiated_fixtures = false
#
-# fixtures :foos
-#
-# def test_godzilla
+# test "godzilla" do
# assert !Foo.find(:all).empty?
# Foo.destroy_all
# assert Foo.find(:all).empty?
# end
#
-# def test_godzilla_aftermath
+# test "godzilla aftermath" do
# assert !Foo.find(:all).empty?
# end
# end
@@ -220,24 +208,25 @@ end
# access to fixture data for every table that has been loaded through fixtures (depending on the value of +use_instantiated_fixtures+)
#
# When *not* to use transactional fixtures:
-# 1. You're testing whether a transaction works correctly. Nested transactions don't commit until all parent transactions commit,
-# particularly, the fixtures transaction which is begun in setup and rolled back in teardown. Thus, you won't be able to verify
-# the results of your transaction until Active Record supports nested transactions or savepoints (in progress).
-# 2. Your database does not support transactions. Every Active Record database supports transactions except MySQL MyISAM.
-# Use InnoDB, MaxDB, or NDB instead.
+#
+# 1. You're testing whether a transaction works correctly. Nested transactions don't commit until all parent transactions commit,
+# particularly, the fixtures transaction which is begun in setup and rolled back in teardown. Thus, you won't be able to verify
+# the results of your transaction until Active Record supports nested transactions or savepoints (in progress).
+# 2. Your database does not support transactions. Every Active Record database supports transactions except MySQL MyISAM.
+# Use InnoDB, MaxDB, or NDB instead.
#
# = Advanced YAML Fixtures
#
# YAML fixtures that don't specify an ID get some extra features:
#
-# * Stable, autogenerated ID's
+# * Stable, autogenerated IDs
# * Label references for associations (belongs_to, has_one, has_many)
# * HABTM associations as inline lists
# * Autofilled timestamp columns
# * Fixture label interpolation
# * Support for YAML defaults
#
-# == Stable, autogenerated ID's
+# == Stable, autogenerated IDs
#
# Here, have a monkey fixture:
#
@@ -292,7 +281,7 @@ end
#
# Add a few more monkeys and pirates and break this into multiple files,
# and it gets pretty hard to keep track of what's going on. Let's
-# use labels instead of ID's:
+# use labels instead of IDs:
#
# ### in pirates.yml
#
diff --git a/railties/guides/files/javascripts/code_highlighter.js b/railties/guides/files/javascripts/code_highlighter.js
index a234ba888c..ce983dad52 100755
--- a/railties/guides/files/javascripts/code_highlighter.js
+++ b/railties/guides/files/javascripts/code_highlighter.js
@@ -185,4 +185,4 @@ CodeHighlighter.init = function() {
for (var i=0; i < this.styleSets.length; i++) {
highlightCode(this.styleSets[i]);
}
-}
\ No newline at end of file
+}
diff --git a/railties/guides/files/stylesheets/main.css b/railties/guides/files/stylesheets/main.css
index 64243be945..f8302505c5 100644
--- a/railties/guides/files/stylesheets/main.css
+++ b/railties/guides/files/stylesheets/main.css
@@ -7,416 +7,549 @@
/* General
--------------------------------------- */
-.left {float: left; margin-right: 1em;}
-.right {float: right; margin-left: 1em;}
-.small {font-size: smaller;}
-.large {font-size: larger;}
-.hide {display: none;}
+.left {
+ float: left;
+ margin-right: 1em;
+}
+.right {
+ float: right;
+ margin-left: 1em;
+}
+.small {
+ font-size: smaller;
+}
+.large {
+ font-size: larger;
+}
+.hide {
+ display: none;
+}
-li ul, li ol { margin:0 1.5em; }
-ul, ol { margin: 0 1.5em 1.5em 1.5em; }
+li ul, li ol {
+ margin: 0 1.5em;
+}
+ul, ol {
+ margin: 0 1.5em 1.5em 1.5em;
+}
-ul { list-style-type: disc; }
-ol { list-style-type: decimal; }
+ul {
+ list-style-type: disc;
+}
+ol {
+ list-style-type: decimal;
+}
-dl { margin: 0 0 1.5em 0; }
-dl dt { font-weight: bold; }
-dd { margin-left: 1.5em;}
+dl {
+ margin: 0 0 1.5em 0;
+}
+dl dt {
+ font-weight: bold;
+}
+dd {
+ margin-left: 1.5em;
+}
-pre,code { margin: 1.5em 0; white-space: pre; }
-pre,code,tt { font: 1em 'andale mono', 'lucida console', monospace; line-height: 1.5; }
+pre,code {
+ margin: 1.5em 0;
+ white-space: pre;
+}
+pre,code {
+ font: 1em 'andale mono', 'lucida console', monospace;
+ line-height: 1.5;
+}
-abbr, acronym { border-bottom: 1px dotted #666; }
-address { margin: 0 0 1.5em; font-style: italic; }
-del { color:#666; }
+abbr, acronym {
+ border-bottom: 1px dotted #666;
+}
+address {
+ margin: 0 0 1.5em;
+ font-style: italic;
+}
+del {
+ color: #666;
+}
-blockquote { margin: 1.5em; color: #666; font-style: italic; }
-strong { font-weight: bold; }
-em, dfn { font-style: italic; }
-dfn { font-weight: bold; }
-sup, sub { line-height: 0; }
-p {margin: 0 0 1.5em;}
+blockquote {
+ margin: 1.5em;
+ color: #666;
+ font-style: italic;
+}
+strong {
+ font-weight: bold;
+}
+em, dfn {
+ font-style: italic;
+}
+dfn {
+ font-weight: bold;
+}
+sup, sub {
+ line-height: 0;
+}
+p {
+ margin: 0 0 1.5em;
+}
-label { font-weight: bold; }
-fieldset { padding:1.4em; margin: 0 0 1.5em 0; border: 1px solid #ccc; }
-legend { font-weight: bold; font-size:1.2em; }
+label {
+ font-weight: bold;
+}
+fieldset {
+ padding: 1.4em;
+ margin: 0 0 1.5em 0;
+ border: 1px solid #ccc;
+}
+legend {
+ font-weight: bold;
+ font-size: 1.2em;
+}
-input.text, input.title,
-textarea, select {
- margin:0.5em 0;
- border:1px solid #bbb;
+input.text, input.title, textarea, select {
+ margin: 0.5em 0em;
+ border: 1px solid #bbb;
}
table {
- margin: 1em 0;
- border: 1px solid #ddd;
- background: #f4f4f4;
- border-spacing: 0;
+ margin: 1em 0;
+ border: 1px solid #ddd;
+ background: #f4f4f4;
+ border-spacing: 0;
}
- table th, table td {
+table th, table td {
padding: 0.25em;
border-right: 1px dotted #e0e0e0;
border-bottom: 1px dotted #e0e0e0;
- }
+}
- table th:last-child, table td:last-child {
- border-right: none;
- }
+table th:last-child, table td:last-child {
+ border-right: none;
+}
- table th {
+table th {
border-bottom: 1px solid #ddd;
background: #f0f0f0;
font-weight: bold;
- }
+}
- table td {
- }
-
- table tt {
+table tt {
padding: 0.1em;
- }
+}
/* Structure and Layout
--------------------------------------- */
body {
- text-align: center;
- font-family: Helvetica, Arial, sans-serif;
- font-size: 87.5%;
- line-height: 1.5em;
- background: #222;
- color: #999;
- }
+ text-align: center;
+ font-family: Helvetica, Arial, sans-serif;
+ font-size: 87.5%;
+ line-height: 1.5em;
+ background: #222;
+ color: #999;
+}
.wrapper {
- text-align: left;
- margin: 0 auto;
- width: 69em;
- }
+ text-align: left;
+ margin: 0 auto;
+ width: 69em;
+}
#topNav {
- padding: 1em 0;
- color: #565656;
+ padding: 1em 0;
+ color: #565656;
}
#header {
- background: #c52f24 url(../../images/header_tile.gif) repeat-x;
- color: #FFF;
- padding: 1.5em 0;
- position: relative;
- z-index: 99;
- }
+ background: #c52f24 url(../../images/header_tile.gif) repeat-x;
+ color: #FFF;
+ padding: 1.5em 0;
+ position: relative;
+ z-index: 99;
+}
#feature {
- background: #d5e9f6 url(../../images/feature_tile.gif) repeat-x;
- color: #333;
- padding: 0.5em 0 1.5em;
+ background: #d5e9f6 url(../../images/feature_tile.gif) repeat-x;
+ color: #333;
+ padding: 0.5em 0 1.5em;
}
#container {
- background: #FFF;
- color: #333;
- padding: 0.5em 0 1.5em 0;
- }
-
-#mainCol {
- width: 45em;
- margin-left: 2em;
- }
-
-#subCol {
- position: absolute;
- z-index: 0;
- top: 0;
- right: 0;
- background: #FFF;
- padding: 1em 1.5em 1em 1.25em;
- width: 17em;
- font-size: 0.9285em;
- line-height: 1.3846em;
- }
-
-#extraCol {display: none;}
-
-#footer {
- padding: 2em 0;
- background: url(../../images/footer_tile.gif) repeat-x;
- }
-#footer .wrapper {
- padding-left: 2em;
- width: 67em;
+ background: #FFF;
+ color: #333;
+ padding: 0.5em 0 1.5em 0;
}
-#header .wrapper, #topNav .wrapper, #feature .wrapper {padding-left: 1em; width: 68em;}
-#feature .wrapper {width: 45em; padding-right: 23em; position: relative; z-index: 0;}
+#mainCol {
+ width: 45em;
+ margin-left: 2em;
+}
+
+#subCol {
+ position: absolute;
+ z-index: 0;
+ top: 0;
+ right: 0;
+ background: #FFF;
+ padding: 1em 1.5em 1em 1.25em;
+ width: 17em;
+ font-size: 0.9285em;
+ line-height: 1.3846em;
+}
+
+#extraCol {
+ display: none;
+}
+
+#footer {
+ padding: 2em 0;
+ background: url(../../images/footer_tile.gif) repeat-x;
+}
+#footer .wrapper {
+ padding-left: 2em;
+ width: 67em;
+}
+
+#header .wrapper, #topNav .wrapper, #feature .wrapper {
+ padding-left: 1em;
+ width: 68em;
+}
+#feature .wrapper {
+ width: 45em;
+ padding-right: 23em;
+ position: relative;
+ z-index: 0;
+}
/* Links
--------------------------------------- */
a, a:link, a:visited {
- color: #ee3f3f;
- text-decoration: underline;
- }
+ color: #ee3f3f;
+ text-decoration: underline;
+}
-#mainCol a, #subCol a {color: #980905;}
+#mainCol a, #subCol a {
+ color: #980905;
+}
/* Navigation
--------------------------------------- */
-.nav {margin: 0; padding: 0;}
-.nav li {display: inline; list-style: none;}
-
-#header .nav {
- float: right;
- margin-top: 1.5em;
- font-size: 1.2857em;
+.nav {
+ margin: 0;
+ padding: 0;
+}
+.nav li {
+ display: inline;
+ list-style: none;
}
-#header .nav li {margin: 0 0 0 0.5em;}
-#header .nav a {color: #FFF; text-decoration: none;}
-#header .nav a:hover {text-decoration: underline;}
+#header .nav {
+ float: right;
+ margin-top: 1.5em;
+ font-size: 1.2857em;
+}
+
+#header .nav li {
+ margin: 0 0 0 0.5em;
+}
+#header .nav a {
+ color: #FFF;
+ text-decoration: none;
+}
+#header .nav a:hover {
+ text-decoration: underline;
+}
#header .nav .index {
- padding: 0.5em 1.5em;
- border-radius: 1em;
- -webkit-border-radius: 1em;
- -moz-border-radius: 1em;
- background: #980905;
- position: relative;
+ padding: 0.5em 1.5em;
+ border-radius: 1em;
+ -webkit-border-radius: 1em;
+ -moz-border-radius: 1em;
+ background: #980905;
+ position: relative;
}
#header .nav .index a {
- background: #980905 url(../../images/nav_arrow.gif) no-repeat right top;
- padding-right: 1em;
- position: relative;
- z-index: 15;
- padding-bottom: 0.125em;
+ background: #980905 url(../../images/nav_arrow.gif) no-repeat right top;
+ padding-right: 1em;
+ position: relative;
+ z-index: 15;
+ padding-bottom: 0.125em;
+}
+#header .nav .index:hover a, #header .nav .index a:hover {
+ background-position: right -81px;
}
-#header .nav .index:hover a, #header .nav .index a:hover {background-position: right -81px;}
#guides {
- width: 27em;
- display: block;
- background: #980905;
- border-radius: 1em;
- -webkit-border-radius: 1em;
- -moz-border-radius: 1em;
- -webkit-box-shadow: 0.25em 0.25em 1em rgba(0,0,0,0.25);
- -moz-box-shadow: rgba(0,0,0,0.25) 0.25em 0.25em 1em;
- color: #f1938c;
- padding: 1.5em 2em;
- position: absolute;
- z-index: 10;
- top: -0.25em;
- right: 0;
- padding-top: 2em;
+ width: 27em;
+ display: block;
+ background: #980905;
+ border-radius: 1em;
+ -webkit-border-radius: 1em;
+ -moz-border-radius: 1em;
+ -webkit-box-shadow: 0.25em 0.25em 1em rgba(0,0,0,0.25);
+ -moz-box-shadow: rgba(0,0,0,0.25) 0.25em 0.25em 1em;
+ color: #f1938c;
+ padding: 1.5em 2em;
+ position: absolute;
+ z-index: 10;
+ top: -0.25em;
+ right: 0;
+ padding-top: 2em;
}
#guides dt, #guides dd {
- font-weight: normal;
- font-size: 0.722em;
- margin: 0;
- padding: 0;
+ font-weight: normal;
+ font-size: 0.722em;
+ margin: 0;
+ padding: 0;
+}
+#guides dt {
+ padding: 0;
+ margin: 0.5em 0 0;
+}
+#guides a {
+ color: #FFF;
+ background: none !important;
+}
+#guides .L, #guides .R {
+ float: left;
+ width: 50%;
+ margin: 0;
+ padding: 0;
+}
+#guides .R {
+ float: right;
}
-#guides dt {padding:0; margin: 0.5em 0 0;}
-#guides a {color: #FFF; background: none !important;}
-#guides .L, #guides .R {float: left; width: 50%; margin: 0; padding: 0;}
-#guides .R {float: right;}
#guides hr {
- display: block;
- border: none;
- height: 1px;
- color: #f1938c;
- background: #f1938c;
+ display: block;
+ border: none;
+ height: 1px;
+ color: #f1938c;
+ background: #f1938c;
}
/* Headings
--------------------------------------- */
h1 {
- font-size: 2.5em;
- line-height: 1em;
- margin: 0.6em 0 .2em;
- font-weight: bold;
- }
+ font-size: 2.5em;
+ line-height: 1em;
+ margin: 0.6em 0 .2em;
+ font-weight: bold;
+}
h2 {
- font-size: 2.1428em;
- line-height: 1em;
- margin: 0.7em 0 .2333em;
- font-weight: bold;
- }
+ font-size: 2.1428em;
+ line-height: 1em;
+ margin: 0.7em 0 .2333em;
+ font-weight: bold;
+}
h3 {
- font-size: 1.7142em;
- line-height: 1.286em;
- margin: 0.875em 0 0.2916em;
- font-weight: bold;
- }
+ font-size: 1.7142em;
+ line-height: 1.286em;
+ margin: 0.875em 0 0.2916em;
+ font-weight: bold;
+}
h4 {
- font-size: 1.2857em;
- line-height: 1.2em;
- margin: 1.6667em 0 .3887em;
- font-weight: bold;
- }
+ font-size: 1.2857em;
+ line-height: 1.2em;
+ margin: 1.6667em 0 .3887em;
+ font-weight: bold;
+}
h5 {
- font-size: 1em;
- line-height: 1.5em;
- margin: 1em 0 .5em;
- font-weight: bold;
+ font-size: 1em;
+ line-height: 1.5em;
+ margin: 1em 0 .5em;
+ font-weight: bold;
}
h6 {
- font-size: 1em;
- line-height: 1.5em;
- margin: 1em 0 .5em;
- font-weight: normal;
- }
+ font-size: 1em;
+ line-height: 1.5em;
+ margin: 1em 0 .5em;
+ font-weight: normal;
+}
/* Content
--------------------------------------- */
.pic {
- margin: 0 2em 2em 0;
+ margin: 0 2em 2em 0;
}
-#topNav strong {color: #999; margin-right: 0.5em;}
-#topNav strong a {color: #FFF;}
+#topNav strong {
+ color: #999;
+ margin-right: 0.5em;
+}
+#topNav strong a {
+ color: #FFF;
+}
#header h1 {
- float: left;
- background: url(../../images/ruby_guides_logo.gif) no-repeat;
- width: 492px;
- text-indent: -9999em;
- margin: 0;
- padding: 0;
+ float: left;
+ background: url(../../images/ruby_guides_logo.gif) no-repeat;
+ width: 492px;
+ text-indent: -9999em;
+ margin: 0;
+ padding: 0;
}
#header h1 a {
- text-decoration: none;
- display: block;
- height: 77px;
+ text-decoration: none;
+ display: block;
+ height: 77px;
}
#feature p {
- font-size: 1.2857em;
- margin-bottom: 0.75em;
+ font-size: 1.2857em;
+ margin-bottom: 0.75em;
}
-#feature ul {margin-left: 0;}
+#feature ul {
+ margin-left: 0;
+}
#feature ul li {
- list-style: none;
- background: url(../../images/check_bullet.gif) no-repeat left 0.5em;
- padding: 0.5em 1.75em 0.5em 1.75em;
- font-size: 1.1428em;
- font-weight: bold;
+ list-style: none;
+ background: url(../../images/check_bullet.gif) no-repeat left 0.5em;
+ padding: 0.5em 1.75em 0.5em 1.75em;
+ font-size: 1.1428em;
+ font-weight: bold;
}
#mainCol dd, #subCol dd {
- padding: 0.25em 0 1em;
- border-bottom: 1px solid #CCC;
- margin-bottom: 1em;
- margin-left: 0;
- padding-left: 28px;
+ padding: 0.25em 0 1em;
+ border-bottom: 1px solid #CCC;
+ margin-bottom: 1em;
+ margin-left: 0;
+ padding-left: 28px;
}
#mainCol dt, #subCol dt {
- font-size: 1.2857em;
- padding: 0.125em 0 0.25em 28px;
- margin-bottom: 0;
- background: url(../../images/book_icon.gif) no-repeat left top;
+ font-size: 1.2857em;
+ padding: 0.125em 0 0.25em 28px;
+ margin-bottom: 0;
+ background: url(../../images/book_icon.gif) no-repeat left top;
}
#mainCol dd.ticket, #subCol dd.ticket {
- background: #fff9d8 url(../../images/tab_yellow.gif) no-repeat left top;
- border: none;
- padding: 1.25em 1em 1.25em 48px;
- margin-left: 0;
- margin-top: 0.25em;
+ background: #fff9d8 url(../../images/tab_yellow.gif) no-repeat left top;
+ border: none;
+ padding: 1.25em 1em 1.25em 48px;
+ margin-left: 0;
+ margin-top: 0.25em;
}
#mainCol dd.warning, #subCol dd.warning {
- background: #f9d9d8 url(../../images/tab_red.gif) no-repeat left top;
- border: none;
- padding: 1.25em 1.25em 1.25em 48px;
- margin-left: 0;
- margin-top: 0.25em;
+ background: #f9d9d8 url(../../images/tab_red.gif) no-repeat left top;
+ border: none;
+ padding: 1.25em 1.25em 1.25em 48px;
+ margin-left: 0;
+ margin-top: 0.25em;
}
-#subCol .chapters {color: #980905;}
-#subCol .chapters a {font-weight: bold;}
-#subCol .chapters ul a {font-weight: normal;}
-#subCol .chapters li {margin-bottom: 0.75em;}
-#subCol h3.chapter {margin-top: 0.25em;}
-#subCol h3.chapter img {vertical-align: text-bottom;}
-#subCol .chapters ul {margin-left: 0; margin-top: 0.5em;}
+#subCol .chapters {
+ color: #980905;
+}
+#subCol .chapters a {
+ font-weight: bold;
+}
+#subCol .chapters ul a {
+ font-weight: normal;
+}
+#subCol .chapters li {
+ margin-bottom: 0.75em;
+}
+#subCol h3.chapter {
+ margin-top: 0.25em;
+}
+#subCol h3.chapter img {
+ vertical-align: text-bottom;
+}
+#subCol .chapters ul {
+ margin-left: 0;
+ margin-top: 0.5em;
+}
#subCol .chapters ul li {
- list-style: none;
- padding: 0 0 0 1em;
- background: url(../../images/bullet.gif) no-repeat left 0.45em;
- margin-left: 0;
- font-size: 1em;
- font-weight: normal;
+ list-style: none;
+ padding: 0 0 0 1em;
+ background: url(../../images/bullet.gif) no-repeat left 0.45em;
+ margin-left: 0;
+ font-size: 1em;
+ font-weight: normal;
+}
+#subCol .chapters p {
+ font-size: 1em;
}
tt {
- background: #EEE;
- border: 1px solid #CCC;
- padding: 0.25em 0.5em;
- font-family: monaco, "Bitstream Vera Sans Mono", "Courier New", courier, monospace;
+ font-family: monaco, "Bitstream Vera Sans Mono", "Courier New", courier, monospace;
}
code, pre {
- font-family: monaco, "Bitstream Vera Sans Mono", "Courier New", courier, monospace;
- background: #EEE url(../../images/tab_grey.gif) no-repeat left top;
- border: none;
- padding: 0.25em 1em 0.5em 48px;
- margin-left: 0;
- margin-top: 0.25em;
- display: block;
+ font-family: monaco, "Bitstream Vera Sans Mono", "Courier New", courier, monospace;
+ background: #EEE url(../../images/tab_grey.gif) no-repeat left top;
+ border: none;
+ padding: 0.25em 1em 0.5em 48px;
+ margin-left: 0;
+ margin-top: 0.25em;
+ display: block;
+ min-height: 45px;
+ overflow: auto;
+}
+
+.info code, .info pre {
+ background-image: none;
+ background-color: #C5D9E6;
+ padding: 0.25em 1em;
+ min-height: 1px;
}
.note {
- background: #fff9d8 url(../../images/tab_note.gif) no-repeat left top;
- border: none;
- padding: 1em 1em 0.25em 48px;
- margin-left: 0;
- margin-top: 0.25em;
+ background: #fff9d8 url(../../images/tab_note.gif) no-repeat left top;
+ border: none;
+ padding: 1em 1em 0.25em 48px;
+ margin-left: 0;
+ margin-top: 0.25em;
}
.info {
- background: #d5e9f6 url(../../images/tab_info.gif) no-repeat left top;
- border: none;
- padding: 1em 1em 0.25em 48px;
- margin-left: 0;
- margin-top: 0.25em;
+ background: #d5e9f6 url(../../images/tab_info.gif) no-repeat left top;
+ border: none;
+ padding: 1em 1em 0.25em 48px;
+ margin-left: 0;
+ margin-top: 0.25em;
}
.warning {
- background: #f9d9d8 url(../../images/tab_red.gif) no-repeat left top;
- border: none;
- padding: 1em 1em 0.25em 48px;
- margin-left: 0;
- margin-top: 0.25em;
+ background: #f9d9d8 url(../../images/tab_red.gif) no-repeat left top;
+ border: none;
+ padding: 1em 1em 0.25em 48px;
+ margin-left: 0;
+ margin-top: 0.25em;
}
-.warning tt, .note tt, .info tt {border:none; background: none; padding: 0;}
+.warning tt, .note tt, .info tt {
+ border: none;
+ background: none;
+ padding: 0;
+}
em.highlight {
- background: #fffcdb;
- padding: 0 0.25em;
+ background: #fffcdb;
+ padding: 0 0.25em;
}
#mainCol ul li {
- list-style:none;
- background: url(../../images/grey_bullet.gif) no-repeat left 0.5em;
- padding-left: 1em;
- margin-left: 0;
+ list-style: none;
+ background: url(../../images/grey_bullet.gif) no-repeat left 0.5em;
+ padding-left: 1em;
+ margin-left: 0;
}
/* Clearing
@@ -430,7 +563,15 @@ em.highlight {
visibility: hidden;
}
-.clearfix {display: inline-block;}
-* html .clearfix {height: 1%;}
-.clearfix {display: block;}
-.clear { clear:both; }
+.clearfix {
+ display: inline-block;
+}
+* html .clearfix {
+ height: 1%;
+}
+.clearfix {
+ display: block;
+}
+.clear {
+ clear: both;
+}
diff --git a/railties/guides/files/stylesheets/print.css b/railties/guides/files/stylesheets/print.css
index 98780ab029..628da105d4 100755
--- a/railties/guides/files/stylesheets/print.css
+++ b/railties/guides/files/stylesheets/print.css
@@ -49,4 +49,4 @@ blockquote {
}
.small { font-size: .9em; }
-.large { font-size: 1.1em; }
\ No newline at end of file
+.large { font-size: 1.1em; }
diff --git a/railties/guides/files/stylesheets/reset.css b/railties/guides/files/stylesheets/reset.css
index cd5f8e846e..cb14fbcc55 100755
--- a/railties/guides/files/stylesheets/reset.css
+++ b/railties/guides/files/stylesheets/reset.css
@@ -40,4 +40,4 @@ blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
-}
\ No newline at end of file
+}
diff --git a/railties/guides/files/stylesheets/syntax.css b/railties/guides/files/stylesheets/syntax.css
index cf5df03494..55fc5b209f 100644
--- a/railties/guides/files/stylesheets/syntax.css
+++ b/railties/guides/files/stylesheets/syntax.css
@@ -28,4 +28,4 @@
.ruby .symbol {
color: green;
-}
\ No newline at end of file
+}
diff --git a/railties/guides/rails_guides.rb b/railties/guides/rails_guides.rb
index 7617dff5a3..6da7de890e 100644
--- a/railties/guides/rails_guides.rb
+++ b/railties/guides/rails_guides.rb
@@ -3,12 +3,13 @@ $: << pwd
$: << File.join(pwd, "../../activesupport/lib")
$: << File.join(pwd, "../../actionpack/lib")
+require "action_controller"
require "action_view"
# Require rubygems after loading Action View
require 'rubygems'
begin
- gem 'RedCloth', '= 4.1.1'# Need exactly 4.1.1
+ gem 'RedCloth', '>= 4.1.1'# Need exactly 4.1.1
rescue Gem::LoadError
$stderr.puts %(Missing the RedCloth 4.1.1 gem.\nPlease `gem install -v=4.1.1 RedCloth` to generate the guides.)
exit 1
diff --git a/railties/guides/rails_guides/generator.rb b/railties/guides/rails_guides/generator.rb
index 3cbc2eb1f4..41d22e37fd 100644
--- a/railties/guides/rails_guides/generator.rb
+++ b/railties/guides/rails_guides/generator.rb
@@ -25,29 +25,7 @@ module RailsGuides
end
guides.each do |guide|
- guide =~ /(.*?)(\.erb)?\.textile/
- name = $1
-
- puts "Generating #{name}"
-
- file = File.join(output, "#{name}.html")
- File.open(file, 'w') do |f|
- @view = ActionView::Base.new(view_path)
- @view.extend(Helpers)
-
- if guide =~ /\.erb\.textile/
- # Generate the erb pages with textile formatting - e.g. index/authors
- result = view.render(:layout => 'layout', :file => guide)
- f.write textile(result)
- else
- body = File.read(File.join(view_path, guide))
- body = set_header_section(body, @view)
- body = set_index(body, @view)
-
- result = view.render(:layout => 'layout', :text => textile(body))
- f.write result
- end
- end
+ generate_guide(guide)
end
# Copy images and css files to html directory
@@ -55,6 +33,32 @@ module RailsGuides
FileUtils.cp_r File.join(guides_dir, 'files'), File.join(output, 'files')
end
+ def generate_guide(guide)
+ guide =~ /(.*?)(\.erb)?\.textile/
+ name = $1
+
+ puts "Generating #{name}"
+
+ file = File.join(output, "#{name}.html")
+ File.open(file, 'w') do |f|
+ @view = ActionView::Base.new(view_path)
+ @view.extend(Helpers)
+
+ if guide =~ /\.erb\.textile/
+ # Generate the erb pages with textile formatting - e.g. index/authors
+ result = view.render(:layout => 'layout', :file => name)
+ f.write textile(result)
+ else
+ body = File.read(File.join(view_path, guide))
+ body = set_header_section(body, @view)
+ body = set_index(body, @view)
+
+ result = view.render(:layout => 'layout', :text => textile(body))
+ f.write result
+ end
+ end
+ end
+
def set_header_section(body, view)
new_body = body.gsub(/(.*?)endprologue\./m, '').strip
header = $1
@@ -72,21 +76,19 @@ module RailsGuides
def set_index(body, view)
index = <<-INDEX
-
Chapters
+
Chapters
- INDEX
+ INDEX
i = Indexer.new(body)
i.index
# Set index for 2 levels
i.level_hash.each do |key, value|
- bookmark = '#' + key.gsub(/[^a-z0-9\-_\+]+/i, '').underscore.dasherize
- link = view.content_tag(:a, :href => bookmark) { key }
+ link = view.content_tag(:a, :href => key[:id]) { textile(key[:title]) }
children = value.keys.map do |k|
- bm = '#' + k.gsub(/[^a-z0-9\-_\+]+/i, '').underscore.dasherize
- l = view.content_tag(:a, :href => bm) { k }
+ l = view.content_tag(:a, :href => k[:id]) { textile(k[:title]) }
view.content_tag(:li, l)
end
diff --git a/railties/guides/rails_guides/indexer.rb b/railties/guides/rails_guides/indexer.rb
index 52809829e2..7cb254d0b0 100644
--- a/railties/guides/rails_guides/indexer.rb
+++ b/railties/guides/rails_guides/indexer.rb
@@ -29,13 +29,17 @@ module RailsGuides
return level_hash
elsif level == current_level
index = counters.join(".")
- bookmark = '#' + title.gsub(/[^a-z0-9\-_\+]+/i, '').underscore.dasherize
+ bookmark = '#' + title.gsub(/[^a-z0-9\-_]+/i, '').underscore.dasherize
raise "Parsing Fail" unless @result.sub!(matched, "h#{level}(#{bookmark}). #{index}#{title}")
+ key = {
+ :title => title,
+ :id => bookmark
+ }
# Recurse
counters << 1
- level_hash[title.strip] = process(s.post_match, current_level + 1, counters)
+ level_hash[key] = process(s.post_match, current_level + 1, counters)
counters.pop
# Increment the current level
diff --git a/railties/guides/rails_guides/textile_extensions.rb b/railties/guides/rails_guides/textile_extensions.rb
index a7fc5ba49c..7ebbf0090e 100644
--- a/railties/guides/rails_guides/textile_extensions.rb
+++ b/railties/guides/rails_guides/textile_extensions.rb
@@ -25,6 +25,9 @@ module RailsGuides
body.gsub!(/\+(.*?)\+/) do |m|
"#{$1}"
end
+
+ # The real plus sign
+ body.gsub!('', '+')
end
def code(body)
diff --git a/railties/guides/source/2_2_release_notes.textile b/railties/guides/source/2_2_release_notes.textile
index 6f882f6d7d..f60af01050 100644
--- a/railties/guides/source/2_2_release_notes.textile
+++ b/railties/guides/source/2_2_release_notes.textile
@@ -164,9 +164,9 @@ h4. New Dynamic Finders
Two new sets of methods have been added to Active Record's dynamic finders family.
-h5. +find_last_by_+
+h5. +find_last_by_attribute+
-The +find_last_by_+ method is equivalent to +Model.last(:conditions => {:attribute => value})+
+The +find_last_by_attribute+ method is equivalent to +Model.last(:conditions => {:attribute => value})+
# Get the last user who signed up from London
@@ -175,9 +175,9 @@ User.find_last_by_city('London')
* Lead Contributor: "Emilio Tagua":http://www.workingwithrails.com/person/9147-emilio-tagua
-h5. +find_by_!+
+h5. +find_by_attribute!+
-The new bang! version of +find_by_!+ is equivalent to +Model.first(:conditions => {:attribute => value}) || raise ActiveRecord::RecordNotFound+ Instead of returning +nil+ if it can't find a matching record, this method will raise an exception if it cannot find a match.
+The new bang! version of +find_by_attribute!+ is equivalent to +Model.first(:conditions => {:attribute => value}) || raise ActiveRecord::RecordNotFound+ Instead of returning +nil+ if it can't find a matching record, this method will raise an exception if it cannot find a match.
# Raise ActiveRecord::RecordNotFound exception if 'Moby' hasn't signed up yet!
diff --git a/railties/guides/source/2_3_release_notes.textile b/railties/guides/source/2_3_release_notes.textile
index 334416f3f6..4734e32606 100644
--- a/railties/guides/source/2_3_release_notes.textile
+++ b/railties/guides/source/2_3_release_notes.textile
@@ -42,7 +42,7 @@ Here's a summary of the rack-related changes:
h4. Renewed Support for Rails Engines
-After some versions without an upgrade, Rails 2.3 offers some new features for Rails Engines (Rails applications that can be embedded within other applications). First, routing files in engines are automatically loaded and reloaded now, just like your +routes.rb+ file (this also applies to routing files in other plugins). Second, if your plugin has an app folder, then app/[models|controllers|helpers] will automatically be added to the Rails load path. Engines also support adding view paths now.
+After some versions without an upgrade, Rails 2.3 offers some new features for Rails Engines (Rails applications that can be embedded within other applications). First, routing files in engines are automatically loaded and reloaded now, just like your +routes.rb+ file (this also applies to routing files in other plugins). Second, if your plugin has an app folder, then app/[models|controllers|helpers] will automatically be added to the Rails load path. Engines also support adding view paths now, and Action Mailer as well as Action View will use views from engines and other plugins.
h3. Documentation
@@ -50,9 +50,13 @@ The "Ruby on Rails guides":http://guides.rubyonrails.org/ project has published
* More Information: "Rails Documentation Projects":http://weblog.rubyonrails.org/2009/1/15/rails-documentation-projects
+h3. Ruby 1.9.1 Support
+
+Rails 2.3 should pass all of its own tests whether you are running on Ruby 1.8 or the now-released Ruby 1.9.1. You should be aware, though, that moving to 1.9.1 entails checking all of the data adapters, plugins, and other code that you depend on for Ruby 1.9.1 compatibility, as well as Rails core.
+
h3. Active Record
-Active Record gets quite a number of new features and bug fixes in Rails 2.3. The highlights include nested attributes, nested transactions, dynamic scopes, and default scopes.
+Active Record gets quite a number of new features and bug fixes in Rails 2.3. The highlights include nested attributes, nested transactions, dynamic and default scopes, and batch processing.
h4. Nested Attributes
@@ -69,6 +73,13 @@ end
Turning on nested attributes enables a number of things: automatic (and atomic) saving of a record together with its associated children, child-aware validations, and support for nested forms (discussed later).
+You can also specify requirements for any new records that are added via nested attributes using the +:reject_if+ option:
+
+
+accepts_nested_attributes_for :author,
+ :reject_if => proc { |attributes| attributes['name'].blank? }
+
+
* Lead Contributor: "Eloy Duran":http://www.superalloy.nl/blog/
* More Information: "Nested Model Forms":http://weblog.rubyonrails.org/2009/1/26/nested-model-forms
@@ -115,6 +126,28 @@ Rails 2.3 will introduce the notion of _default scopes_ similar to named scopes,
* Lead Contributor: Paweł Kondzior
* More Information: "What's New in Edge Rails: Default Scoping":http://ryandaigle.com/articles/2008/11/18/what-s-new-in-edge-rails-default-scoping
+h4. Batch Processing
+
+You can now process large numbers of records from an ActiveRecord model with less pressure on memory by using +find_in_batches+:
+
+
+Customer.find_in_batches(:conditions => {:active => true}) do |customer_group|
+ customer_group.each { |customer| customer.update_account_balance! }
+end
+
+
+You can pass most of the +find+ options into +find_in_batches+. However, you cannot specify the order that records will be returned in (they will always be returned in ascending order of primary key, which must be an integer), or use the +:limit+ option. Instead, use the +:batch_size: option, which defaults to 1000, to set the number of records that will be returned in each batch.
+
+The new +each+ method provides a wrapper around +find_in_batches+ that returns individual records, with the find itself being done in batches (of 1000 by default):
+
+
+Customer.each do |customer|
+ customer.update_account_balance!
+end
+
+
+Note that you should only use this record for batch processing: for small numbers of records (less than 1000), you should just use the regular find methods with your own loop.
+
h4. Multiple Conditions for Callbacks
When using Active Record callbacks, you can now combine +:if+ and +:unless+ options on the same callback, and supply multiple conditions as an array:
@@ -268,6 +301,10 @@ h4. Localized Views
Rails can now provide localized views, depending on the locale that you have set. For example, suppose you have a +Posts+ controller with a +show+ action. By default, this will render +app/views/posts/show.html.erb+. But if you set +I18n.locale = :da+, it will render +app/views/posts/show.da.html.erb+. If the localized template isn't present, the undecorated version will be used. Rails also includes +I18n#available_locales+ and +I18n::SimpleBackend#available_locales+, which return an array of the translations that are available in the current Rails project.
+h4. Partial Scoping for Translations
+
+A change to the translation API makes things easier and less repetitive to write key translations within partials. If you call +translate(".foo")+ from the +people/index.html.erb+ template, you'll actually be calling +I18n.translate("people.index.foo")+ If you don't prepend the key with a period, then the API doesn't scope, just as before.
+
h4. Other Action Controller Changes
* ETag handling has been cleaned up a bit: Rails will now skip sending an ETag header when there's no body to the response or when sending files with +send_file+.
@@ -399,6 +436,7 @@ h4. Other Action View Changes
* Token generation for CSRF protection has been simplified; now Rails uses a simple random string generated by +ActiveSupport::SecureRandom+ rather than mucking around with session IDs.
* +auto_link+ now properly applies options (such as +:target+ and +:class+) to generated e-mail links.
* The +autolink+ helper has been refactored to make it a bit less messy and more intuitive.
+* +current_page?+ now works properly even when there are multiple query parameters in the URL.
h3. Active Support
@@ -406,7 +444,7 @@ Active Support has a few interesting changes, including the introduction of +Obj
h4. Object#try
-A lot of folks have adopted the notion of using try() to attempt operations on objects. It's especially helpful in views where you can avoid nil-checking by writing code like +<%= @person.try(:name) %>+. Well, now it's baked right into Rails. As implemented in Rails, it raises +NoMethodError+ on private methods and always returns +nil+ if the object is nil.
+A lot of folks have adopted the notion of using try() to attempt operations on objects. It's especially helpful in views where you can avoid nil-checking by writing code like +<%= @person.try(:name) %>+. Well, now it's baked right into Rails. As implemented in Rails, it raises +NoMethodError+ on private methods and always returns +nil+ if the object is nil.
* More Information: "try()":http://ozmm.org/posts/try.html.
@@ -482,6 +520,9 @@ h4. Other Railties Changes
* The dbconsole script now lets you use an all-numeric password without crashing.
* +Rails.root+ now returns a +Pathname+ object, which means you can use it directly with the +join+ method to "clean up existing code":http://afreshcup.com/2008/12/05/a-little-rails_root-tidiness/ that uses +File.join+.
* Various files in /public that deal with CGI and FCGI dispatching are no longer generated in every Rails application by default (you can still get them if you need them by adding +--with-dispatches+ when you run the +rails+ command, or add them later with +rake rails:generate_dispatchers+).
+* Rails Guides have been converted from AsciiDoc to Textile markup.
+* Scaffolded views and controllers have been cleaned up a bit.
+* +script/server+ now accepts a --path argument to mount a Rails application from a specific path.
h3. Deprecated
@@ -495,7 +536,9 @@ A few pieces of older code are deprecated in this release:
* The +:digest+ and +:secret+ options to +protect_from_forgery+ are deprecated and have no effect.
* Some integration test helpers have been removed. +response.headers["Status"]+ and +headers["Status"]+ will no longer return anything. Rack does not allow "Status" in its return headers. However you can still use the +status+ and +status_message+ helpers. +response.headers["cookie"]+ and +headers["cookie"]+ will no longer return any CGI cookies. You can inspect +headers["Set-Cookie"]+ to see the raw cookie header or use the +cookies+ helper to get a hash of the cookies sent to the client.
* +formatted_polymorphic_url+ is deprecated. Use +polymorphic_url+ with +:format+ instead.
+* The +:http_only+ option in +ActionController::Response#set_cookie+ has been renamed to +:httponly+.
+* The +:connector+ and +:skip_last_comma+ options of +to_sentence+ have been replaced by +:words_connnector+, +:two_words_connector+, and +:last_word_connector+ options.
h3. Credits
-Release notes compiled by "Mike Gunderloy":http://afreshcup.com
+Release notes compiled by "Mike Gunderloy":http://afreshcup.com. This version of the Rails 2.3 release notes was compiled based on RC2 of Rails 2.3.
diff --git a/railties/guides/source/action_controller_overview.textile b/railties/guides/source/action_controller_overview.textile
index 31a9c6819c..949a962b27 100644
--- a/railties/guides/source/action_controller_overview.textile
+++ b/railties/guides/source/action_controller_overview.textile
@@ -12,9 +12,9 @@ In this guide you will learn how controllers work and how they fit into the requ
endprologue.
-h3. What Does a Controller do?
+h3. What Does a Controller Do?
-Action Controller is the C in MVC. After routing has determined which controller to use for a request, your controller is responsible for making sense of the request and producing the appropriate output. Luckily, Action Controller does most of the groundwork for you and uses smart conventions to make this as straight-forward as possible.
+Action Controller is the C in MVC. After routing has determined which controller to use for a request, your controller is responsible for making sense of the request and producing the appropriate output. Luckily, Action Controller does most of the groundwork for you and uses smart conventions to make this as straightforward as possible.
For most conventional RESTful applications, the controller will receive the request (this is invisible to you as the developer), fetch or save data from a model and use a view to create HTML output. If your controller needs to do things a little differently, that's not a problem, this is just the most common way for a controller to work.
@@ -24,31 +24,16 @@ NOTE: For more details on the routing process, see "Rails Routing from the Outsi
h3. Methods and Actions
-A controller is a Ruby class which inherits from ApplicationController and has methods just like any other class. When your application receives a request, the routing will determine which controller and action to run, then Rails creates an instance of that controller and runs the public method with the same name as the action.
+A controller is a Ruby class which inherits from +ApplicationController+ and has methods just like any other class. When your application receives a request, the routing will determine which controller and action to run, then Rails creates an instance of that controller and runs the method with the same name as the action.
class ClientsController < ApplicationController
-
- # Actions are public methods
def new
end
-
- # Action methods are responsible for producing output
- def edit
- end
-
-# Helper methods are private and can not be used as actions
-private
-
- def foo
- end
-
end
-There's no rule saying a method on a controller has to be an action; they may well be used for other purposes such as filters, which will be covered later in this guide.
-
-As an example, if a user goes to +/clients/new+ in your application to add a new client, Rails will create an instance of ClientsController and run the +new+ method. Note that the empty method from the example above could work just fine because Rails will by default render the +new.html.erb+ view unless the action says otherwise. The +new+ method could make available to the view a +@client+ instance variable by creating a new Client:
+As an example, if a user goes to +/clients/new+ in your application to add a new client, Rails will create an instance of +ClientsController+ and run the +new+ method. Note that the empty method from the example above could work just fine because Rails will by default render the +new.html.erb+ view unless the action says otherwise. The +new+ method could make available to the view a +@client+ instance variable by creating a new +Client+:
def new
@@ -58,54 +43,55 @@ end
The "Layouts & rendering guide":layouts_and_rendering.html explains this in more detail.
-ApplicationController inherits from ActionController::Base, which defines a number of helpful methods. This guide will cover some of these, but if you're curious to see what's in there, you can see all of them in the API documentation or in the source itself.
++ApplicationController+ inherits from +ActionController::Base+, which defines a number of helpful methods. This guide will cover some of these, but if you're curious to see what's in there, you can see all of them in the API documentation or in the source itself.
+Only public methods are callable as actions. It is a best practice to lower the visibility of methods which are not intended to be actions, like auxiliary methods or filters.
h3. Parameters
-You will probably want to access data sent in by the user or other parameters in your controller actions. There are two kinds of parameters possible in a web application. The first are parameters that are sent as part of the URL, called query string parameters. The query string is everything after "?" in the URL. The second type of parameter is usually referred to as POST data. This information usually comes from a HTML form which has been filled in by the user. It's called POST data because it can only be sent as part of an HTTP POST request. Rails does not make any distinction between query string parameters and POST parameters, and both are available in the +params+ hash in your controller:
+You will probably want to access data sent in by the user or other parameters in your controller actions. There are two kinds of parameters possible in a web application. The first are parameters that are sent as part of the URL, called query string parameters. The query string is everything after "?" in the URL. The second type of parameter is usually referred to as POST data. This information usually comes from an HTML form which has been filled in by the user. It's called POST data because it can only be sent as part of an HTTP POST request. Rails does not make any distinction between query string parameters and POST parameters, and both are available in the +params+ hash in your controller:
class ClientsController < ActionController::Base
-
- # This action uses query string parameters because it gets run by a HTTP
- # GET request, but this does not make any difference to the way in which
- # the parameters are accessed. The URL for this action would look like this
- # in order to list activated clients: /clients?status=activated
+ # This action uses query string parameters because it gets run
+ # by an HTTP GET request, but this does not make any difference
+ # to the way in which the parameters are accessed. The URL for
+ # this action would look like this in order to list activated
+ # clients: /clients?status=activated
def index
- if params[:status] = "activated"
+ if params[:status] == "activated"
@clients = Client.activated
else
- @clients = Client.unativated
+ @clients = Client.unactivated
end
end
- # This action uses POST parameters. They are most likely coming from an HTML
- # form which the user has submitted. The URL for this RESTful request will
- # be "/clients", and the data will be sent as part of the request body.
+ # This action uses POST parameters. They are most likely coming
+ # from an HTML form which the user has submitted. The URL for
+ # this RESTful request will be "/clients", and the data will be
+ # sent as part of the request body.
def create
@client = Client.new(params[:client])
if @client.save
redirect_to @client
else
- # This line overrides the default rendering behavior, which would have been
- # to render the "create" view.
+ # This line overrides the default rendering behavior, which
+ # would have been to render the "create" view.
render :action => "new"
end
end
-
end
-h4. Hash and Array Parameters
+h4. Hash and array parameters
-The params hash is not limited to one-dimensional keys and values. It can contain arrays and (nested) hashes. To send an array of values, append "[]" to the key name:
+The +params+ hash is not limited to one-dimensional keys and values. It can contain arrays and (nested) hashes. To send an array of values, append an empty pair of square brackets "[]" to the key name:
-
+
GET /clients?ids[]=1&ids[]=2&ids[]=3
-
+
-NOTE: The actual URL in this example will be encoded as "/clients?ids%5b%5d=1&ids%5b%5d=2&ids%5b%5b=3" as [ and ] are not allowed in URLs. Most of the time you don't have to worry about this because the browser will take care of it for you, and Rails will decode it back when it receives it, but if you ever find yourself having to send those requests to the server manually you have to keep this in mind.
+NOTE: The actual URL in this example will be encoded as "/clients?ids%5b%5d=1&ids%5b%5d=2&ids%5b%5b=3" as "[" and "]" are not allowed in URLs. Most of the time you don't have to worry about this because the browser will take care of it for you, and Rails will decode it back when it receives it, but if you ever find yourself having to send those requests to the server manually you have to keep this in mind.
The value of +params[:ids]+ will now be +["1", "2", "3"]+. Note that parameter values are always strings; Rails makes no attempt to guess or cast the type.
@@ -120,21 +106,22 @@ To send a hash you include the key name inside the brackets: