diff options
66 files changed, 1765 insertions, 607 deletions
diff --git a/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/obsolete.rb b/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/obsolete.rb index 22b0a126ca..def663b233 100644 --- a/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/obsolete.rb +++ b/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/obsolete.rb @@ -1,6 +1,6 @@ =begin rdoc -= Obsolete methods that are depriciated += Obsolete methods that are deprecated If you really want to see them, go to lib/tmail/obsolete.rb and view to your heart's content. diff --git a/actionpack/lib/action_controller/base/http_authentication.rb b/actionpack/lib/action_controller/base/http_authentication.rb index 2519f55269..525787bf92 100644 --- a/actionpack/lib/action_controller/base/http_authentication.rb +++ b/actionpack/lib/action_controller/base/http_authentication.rb @@ -276,7 +276,7 @@ module ActionController # # The nonce is opaque to the client. Composed of Time, and hash of Time with secret # key from the Rails session secret generated upon creation of project. Ensures - # the time cannot be modifed by client. + # the time cannot be modified by client. def nonce(time = Time.now) t = time.to_i hashed = [t, secret_key] diff --git a/actionpack/lib/action_controller/base/request_forgery_protection.rb b/actionpack/lib/action_controller/base/request_forgery_protection.rb index 6ba86cd0be..ad06657f86 100644 --- a/actionpack/lib/action_controller/base/request_forgery_protection.rb +++ b/actionpack/lib/action_controller/base/request_forgery_protection.rb @@ -106,8 +106,7 @@ module ActionController #:nodoc: !request.content_type.nil? && request.content_type.verify_request? end - # Sets the token value for the current session. Pass a <tt>:secret</tt> option - # in +protect_from_forgery+ to add a custom salt to the hash. + # Sets the token value for the current session. def form_authenticity_token session[:_csrf_token] ||= ActiveSupport::SecureRandom.base64(32) end diff --git a/actionpack/lib/action_controller/routing.rb b/actionpack/lib/action_controller/routing.rb index ce59866531..5b9ded83dd 100644 --- a/actionpack/lib/action_controller/routing.rb +++ b/actionpack/lib/action_controller/routing.rb @@ -139,7 +139,7 @@ module ActionController # # In routes.rb # map.with_options :controller => 'blog' do |blog| # blog.show '', :action => 'list' - # blog.delete 'delete/:id', :action => 'delete', + # blog.delete 'delete/:id', :action => 'delete' # blog.edit 'edit/:id', :action => 'edit' # end # diff --git a/actionpack/lib/action_controller/routing/generation/url_rewriter.rb b/actionpack/lib/action_controller/routing/generation/url_rewriter.rb index 16720b915b..9717582b5e 100644 --- a/actionpack/lib/action_controller/routing/generation/url_rewriter.rb +++ b/actionpack/lib/action_controller/routing/generation/url_rewriter.rb @@ -93,7 +93,7 @@ module ActionController # # * <tt>:only_path</tt> - If true, the relative url is returned. Defaults to +false+. # * <tt>:protocol</tt> - The protocol to connect to. Defaults to 'http'. - # * <tt>:host</tt> - Specifies the host the link should be targetted at. + # * <tt>:host</tt> - Specifies the host the link should be targeted at. # If <tt>:only_path</tt> is false, this option must be # provided either explicitly, or via +default_url_options+. # * <tt>:port</tt> - Optionally specify the port to connect to. diff --git a/actionpack/lib/action_controller/routing/route_set.rb b/actionpack/lib/action_controller/routing/route_set.rb index f5a4b1e1db..040a7e2cb6 100644 --- a/actionpack/lib/action_controller/routing/route_set.rb +++ b/actionpack/lib/action_controller/routing/route_set.rb @@ -155,7 +155,7 @@ module ActionController def define_url_helper(route, name, kind, options) selector = url_helper_name(name, kind) - # The segment keys used for positional paramters + # The segment keys used for positional parameters hash_access_method = hash_access_name(name, kind) diff --git a/actionpack/lib/action_controller/testing/test_case.rb b/actionpack/lib/action_controller/testing/test_case.rb index 7b4eda58e5..a11755b517 100644 --- a/actionpack/lib/action_controller/testing/test_case.rb +++ b/actionpack/lib/action_controller/testing/test_case.rb @@ -56,7 +56,7 @@ module ActionController # # ActionController::TestCase will automatically infer the controller under test # from the test class name. If the controller cannot be inferred from the test - # class name, you can explicity set it with +tests+. + # class name, you can explicitly set it with +tests+. # # class SpecialEdgeCaseWidgetsControllerTest < ActionController::TestCase # tests WidgetController @@ -182,7 +182,7 @@ module ActionController @controller.send(:initialize_current_url) end end - + # Cause the action to be rescued according to the regular rules for rescue_action when the visitor is not local def rescue_action_in_public! @request.remote_addr = '208.77.188.166' # example.com diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb index 72fe9a3232..332743d55b 100644 --- a/actionpack/lib/action_view/helpers/date_helper.rb +++ b/actionpack/lib/action_view/helpers/date_helper.rb @@ -112,12 +112,12 @@ module ActionView # ==== Options # * <tt>:use_month_numbers</tt> - Set to true if you want to use month numbers rather than month names (e.g. # "2" instead of "February"). - # * <tt>:use_short_month</tt> - Set to true if you want to use the abbreviated month name instead of the full - # name (e.g. "Feb" instead of "February"). - # * <tt>:add_month_number</tt> - Set to true if you want to show both, the month's number and name (e.g. + # * <tt>:use_short_month</tt> - Set to true if you want to use abbreviated month names instead of full + # month names (e.g. "Feb" instead of "February"). + # * <tt>:add_month_numbers</tt> - Set to true if you want to use both month numbers and month names (e.g. # "2 - February" instead of "February"). # * <tt>:use_month_names</tt> - Set to an array with 12 month names if you want to customize month names. - # Note: You can also use Rails' new i18n functionality for this. + # Note: You can also use Rails' i18n functionality for this. # * <tt>:date_separator</tt> - Specifies a string to separate the date fields. Default is "" (i.e. nothing). # * <tt>:start_year</tt> - Set the start year for the year select. Default is <tt>Time.now.year - 5</tt>. # * <tt>:end_year</tt> - Set the end year for the year select. Default is <tt>Time.now.year + 5</tt>. @@ -128,7 +128,7 @@ module ActionView # as a hidden field instead of showing a select field. Also note that this implicitly sets :discard_day to true. # * <tt>:discard_year</tt> - Set to true if you don't want to show a year select. This includes the year # as a hidden field instead of showing a select field. - # * <tt>:order</tt> - Set to an array containing <tt>:day</tt>, <tt>:month</tt> and <tt>:year</tt> do + # * <tt>:order</tt> - Set to an array containing <tt>:day</tt>, <tt>:month</tt> and <tt>:year</tt> to # customize the order in which the select fields are shown. If you leave out any of the symbols, the respective # select will not be shown (like when you set <tt>:discard_xxx => true</tt>. Defaults to the order defined in # the respective locale (e.g. [:year, :month, :day] in the en locale that ships with Rails). diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb index 6adbab175f..8cb5882aab 100644 --- a/actionpack/lib/action_view/helpers/form_options_helper.rb +++ b/actionpack/lib/action_view/helpers/form_options_helper.rb @@ -167,31 +167,31 @@ module ActionView # # In addition to the <tt>:include_blank</tt> option documented above, # this method also supports a <tt>:model</tt> option, which defaults - # to TimeZone. This may be used by users to specify a different time - # zone model object. (See +time_zone_options_for_select+ for more - # information.) + # to ActiveSupport::TimeZone. This may be used by users to specify a + # different time zone model object. (See +time_zone_options_for_select+ + # for more information.) # - # You can also supply an array of TimeZone objects + # You can also supply an array of ActiveSupport::TimeZone objects # as +priority_zones+, so that they will be listed above the rest of the - # (long) list. (You can use TimeZone.us_zones as a convenience for - # obtaining a list of the US time zones, or a Regexp to select the zones + # (long) list. (You can use ActiveSupport::TimeZone.us_zones as a convenience + # for obtaining a list of the US time zones, or a Regexp to select the zones # of your choice) # # Finally, this method supports a <tt>:default</tt> option, which selects - # a default TimeZone if the object's time zone is +nil+. + # a default ActiveSupport::TimeZone if the object's time zone is +nil+. # # Examples: # time_zone_select( "user", "time_zone", nil, :include_blank => true) # # time_zone_select( "user", "time_zone", nil, :default => "Pacific Time (US & Canada)" ) # - # time_zone_select( "user", 'time_zone', TimeZone.us_zones, :default => "Pacific Time (US & Canada)") + # time_zone_select( "user", 'time_zone', ActiveSupport::TimeZone.us_zones, :default => "Pacific Time (US & Canada)") # - # time_zone_select( "user", 'time_zone', [ TimeZone['Alaska'], TimeZone['Hawaii'] ]) + # time_zone_select( "user", 'time_zone', [ ActiveSupport::TimeZone['Alaska'], ActiveSupport::TimeZone['Hawaii'] ]) # # time_zone_select( "user", 'time_zone', /Australia/) # - # time_zone_select( "user", "time_zone", TZInfo::Timezone.all.sort, :model => TZInfo::Timezone) + # time_zone_select( "user", "time_zone", ActiveSupport::Timezone.all.sort, :model => ActiveSupport::Timezone) def time_zone_select(object, method, priority_zones = nil, options = {}, html_options = {}) InstanceTag.new(object, method, self, options.delete(:object)).to_time_zone_select_tag(priority_zones, options, html_options) end @@ -393,20 +393,20 @@ module ActionView end # Returns a string of option tags for pretty much any time zone in the - # world. Supply a TimeZone name as +selected+ to have it marked as the - # selected option tag. You can also supply an array of TimeZone objects - # as +priority_zones+, so that they will be listed above the rest of the - # (long) list. (You can use TimeZone.us_zones as a convenience for - # obtaining a list of the US time zones, or a Regexp to select the zones - # of your choice) + # world. Supply a ActiveSupport::TimeZone name as +selected+ to have it + # marked as the selected option tag. You can also supply an array of + # ActiveSupport::TimeZone objects as +priority_zones+, so that they will + # be listed above the rest of the (long) list. (You can use + # ActiveSupport::TimeZone.us_zones as a convenience for obtaining a list + # of the US time zones, or a Regexp to select the zones of your choice) # # The +selected+ parameter must be either +nil+, or a string that names - # a TimeZone. + # a ActiveSupport::TimeZone. # - # By default, +model+ is the TimeZone constant (which can be obtained - # in Active Record as a value object). The only requirement is that the - # +model+ parameter be an object that responds to +all+, and returns - # an array of objects that represent time zones. + # By default, +model+ is the ActiveSupport::TimeZone constant (which can + # be obtained in Active Record as a value object). The only requirement + # is that the +model+ parameter be an object that responds to +all+, and + # returns an array of objects that represent time zones. # # NOTE: Only the option tags are returned, you have to wrap this call in # a regular HTML select tag. diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index ad0733a7e1..c3ce4c671e 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -436,7 +436,7 @@ module ActionView end # Returns the current cycle string after a cycle has been started. Useful - # for complex table highlighing or any other design need which requires + # for complex table highlighting or any other design need which requires # the current cycle string in more than one place. # # ==== Example @@ -544,7 +544,7 @@ module ActionView left, right = $`, $' # detect already linked URLs and URLs in the middle of a tag if left =~ /<[^>]+$/ && right =~ /^[^>]*>/ - # do not change string; URL is alreay linked + # do not change string; URL is already linked href else # don't include trailing punctuation character as part of the URL diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb index de864f453c..c5a6d1f084 100644 --- a/actionpack/lib/action_view/helpers/url_helper.rb +++ b/actionpack/lib/action_view/helpers/url_helper.rb @@ -12,11 +12,11 @@ module ActionView # Returns the URL for the set of +options+ provided. This takes the # same options as +url_for+ in Action Controller (see the - # documentation for ActionController::Base#url_for). Note that by default - # <tt>:only_path</tt> is <tt>true</tt> so you'll get the relative /controller/action - # instead of the fully qualified URL like http://example.com/controller/action. + # documentation for <tt>ActionController::Base#url_for</tt>). Note that by default + # <tt>:only_path</tt> is <tt>true</tt> so you'll get the relative "/controller/action" + # instead of the fully qualified URL like "http://example.com/controller/action". # - # When called from a view, url_for returns an HTML escaped url. If you + # When called from a view, +url_for+ returns an HTML escaped url. If you # need an unescaped url, pass <tt>:escape => false</tt> in the +options+. # # ==== Options @@ -34,8 +34,8 @@ module ActionView # # If you instead of a hash pass a record (like an Active Record or Active Resource) as the options parameter, # you'll trigger the named route for that record. The lookup will happen on the name of the class. So passing - # a Workshop object will attempt to use the workshop_path route. If you have a nested route, such as - # admin_workshop_path you'll have to call that explicitly (it's impossible for url_for to guess that route). + # a Workshop object will attempt to use the +workshop_path+ route. If you have a nested route, such as + # +admin_workshop_path+ you'll have to call that explicitly (it's impossible for +url_for+ to guess that route). # # ==== Examples # <%= url_for(:action => 'index') %> @@ -97,10 +97,10 @@ module ActionView # Creates a link tag of the given +name+ using a URL created by the set # of +options+. See the valid options in the documentation for - # url_for. It's also possible to pass a string instead + # +url_for+. It's also possible to pass a string instead # of an options hash to get a link tag that uses the value of the string as the # href for the link, or use <tt>:back</tt> to link to the referrer - a JavaScript back - # link will be used in place of a referrer if none exists. If nil is passed as + # link will be used in place of a referrer if none exists. If +nil+ is passed as # a name, the link itself will become the name. # # ==== Signatures @@ -117,27 +117,22 @@ module ActionView # * <tt>:popup => true || array of window options</tt> - This will force the # link to open in a popup window. By passing true, a default browser window # will be opened with the URL. You can also specify an array of options - # that are passed-thru to JavaScripts window.open method. + # that are passed to the <tt>window.open</tt> JavaScript call. # * <tt>:method => symbol of HTTP verb</tt> - This modifier will dynamically # create an HTML form and immediately submit the form for processing using # the HTTP verb specified. Useful for having links perform a POST operation # in dangerous actions like deleting a record (which search bots can follow # while spidering your site). Supported verbs are <tt>:post</tt>, <tt>:delete</tt> and <tt>:put</tt>. # Note that if the user has JavaScript disabled, the request will fall back - # to using GET. If you are relying on the POST behavior, you should check - # for it in your controller's action by using the request object's methods - # for <tt>post?</tt>, <tt>delete?</tt> or <tt>put?</tt>. + # to using GET. If <tt>:href => '#'</tt> is used and the user has JavaScript + # disabled clicking the link will have no effect. If you are relying on the + # POST behavior, you should check for it in your controller's action by using + # the request object's methods for <tt>post?</tt>, <tt>delete?</tt> or <tt>put?</tt>. # * The +html_options+ will accept a hash of html attributes for the link tag. # - # Note that if the user has JavaScript disabled, the request will fall back - # to using GET. If <tt>:href => '#'</tt> is used and the user has JavaScript disabled - # clicking the link will have no effect. If you are relying on the POST - # behavior, your should check for it in your controller's action by using the - # request object's methods for <tt>post?</tt>, <tt>delete?</tt> or <tt>put?</tt>. - # # You can mix and match the +html_options+ with the exception of - # <tt>:popup</tt> and <tt>:method</tt> which will raise an ActionView::ActionViewError - # exception. + # <tt>:popup</tt> and <tt>:method</tt> which will raise an + # <tt>ActionView::ActionViewError</tt> exception. # # ==== Examples # Because it relies on +url_for+, +link_to+ supports both older-style controller/action/id arguments @@ -170,9 +165,11 @@ module ActionView # You can use a block as well if your link target is hard to fit into the name parameter. ERb example: # # <% link_to(@profile) do %> - # <strong><%= @profile.name %></strong> -- <span>Check it out!!</span> + # <strong><%= @profile.name %></strong> -- <span>Check it out!</span> # <% end %> - # # => <a href="/profiles/1"><strong>David</strong> -- <span>Check it out!!</span></a> + # # => <a href="/profiles/1"> + # <strong>David</strong> -- <span>Check it out!</span> + # </a> # # Classes and ids for CSS are easy to produce: # @@ -215,7 +212,9 @@ module ActionView # # => <a href="/images/9" onclick="if (confirm('Are you sure?')) { var f = document.createElement('form'); # f.style.display = 'none'; this.parentNode.appendChild(f); f.method = 'POST'; f.action = this.href; # var m = document.createElement('input'); m.setAttribute('type', 'hidden'); m.setAttribute('name', '_method'); - # m.setAttribute('value', 'delete'); f.appendChild(m);f.submit(); };return false;">Delete Image</a> + # m.setAttribute('value', 'delete');var s = document.createElement('input'); s.setAttribute('type', 'hidden'); + # s.setAttribute('name', 'authenticity_token'); s.setAttribute('value', 'Q/ttlxPYZ6R77B+vZ1sBkhj21G2isO9dpE6UtOHBApg='); + # f.appendChild(s)f.appendChild(m);f.submit(); };return false;">Delete Image</a> def link_to(*args, &block) if block_given? options = args.first || {} @@ -246,21 +245,21 @@ module ActionView # by the set of +options+. This is the safest method to ensure links that # cause changes to your data are not triggered by search bots or accelerators. # If the HTML button does not work with your layout, you can also consider - # using the link_to method with the <tt>:method</tt> modifier as described in - # the link_to documentation. + # using the +link_to+ method with the <tt>:method</tt> modifier as described in + # the +link_to+ documentation. # - # The generated FORM element has a class name of <tt>button-to</tt> + # The generated form element has a class name of <tt>button-to</tt> # to allow styling of the form itself and its children. You can control # the form submission and input element behavior using +html_options+. # This method accepts the <tt>:method</tt> and <tt>:confirm</tt> modifiers - # described in the link_to documentation. If no <tt>:method</tt> modifier + # described in the +link_to+ documentation. If no <tt>:method</tt> modifier # is given, it will default to performing a POST operation. You can also # disable the button by passing <tt>:disabled => true</tt> in +html_options+. # If you are using RESTful routes, you can pass the <tt>:method</tt> # to change the HTTP verb used to submit the form. # # ==== Options - # The +options+ hash accepts the same options at url_for. + # The +options+ hash accepts the same options as url_for. # # There are a few special +html_options+: # * <tt>:method</tt> - Specifies the anchor name to be appended to the path. @@ -317,7 +316,7 @@ module ActionView # Creates a link tag of the given +name+ using a URL created by the set of # +options+ unless the current request URI is the same as the links, in # which case only the name is returned (or the given block is yielded, if - # one exists). You can give link_to_unless_current a block which will + # one exists). You can give +link_to_unless_current+ a block which will # specialize the default behavior (e.g., show a "Start Here" link rather # than the link's text). # @@ -343,7 +342,7 @@ module ActionView # <li><a href="/controller/about">About Us</a></li> # </ul> # - # The implicit block given to link_to_unless_current is evaluated if the current + # The implicit block given to +link_to_unless_current+ is evaluated if the current # action is the action given. So, if we had a comments page and wanted to render a # "Go Back" link instead of a link to the comments page, we could do something like this... # @@ -360,7 +359,7 @@ module ActionView # +options+ unless +condition+ is true, in which case only the name is # returned. To specialize the default behavior (i.e., show a login link rather # than just the plaintext link text), you can pass a block that - # accepts the name or the full argument list for link_to_unless. + # accepts the name or the full argument list for +link_to_unless+. # # ==== Examples # <%= link_to_unless(@current_user.nil?, "Reply", { :action => "reply" }) %> @@ -391,8 +390,8 @@ module ActionView # Creates a link tag of the given +name+ using a URL created by the set of # +options+ if +condition+ is true, in which case only the name is # returned. To specialize the default behavior, you can pass a block that - # accepts the name or the full argument list for link_to_unless (see the examples - # in link_to_unless). + # accepts the name or the full argument list for +link_to_unless+ (see the examples + # in +link_to_unless+). # # ==== Examples # <%= link_to_if(@current_user.nil?, "Login", { :controller => "sessions", :action => "new" }) %> @@ -416,27 +415,27 @@ module ActionView # also used as the name of the link unless +name+ is specified. Additional # HTML attributes for the link can be passed in +html_options+. # - # mail_to has several methods for hindering email harvesters and customizing + # +mail_to+ has several methods for hindering email harvesters and customizing # the email itself by passing special keys to +html_options+. # # ==== Options - # * <tt>:encode</tt> - This key will accept the strings "javascript" or "hex". - # Passing "javascript" will dynamically create and encode the mailto: link then + # * <tt>:encode</tt> - This key will accept the strings "javascript" or "hex". + # Passing "javascript" will dynamically create and encode the mailto link then # eval it into the DOM of the page. This method will not show the link on # the page if the user has JavaScript disabled. Passing "hex" will hex - # encode the +email_address+ before outputting the mailto: link. - # * <tt>:replace_at</tt> - When the link +name+ isn't provided, the + # encode the +email_address+ before outputting the mailto link. + # * <tt>:replace_at</tt> - When the link +name+ isn't provided, the # +email_address+ is used for the link label. You can use this option to # obfuscate the +email_address+ by substituting the @ sign with the string # given as the value. - # * <tt>:replace_dot</tt> - When the link +name+ isn't provided, the + # * <tt>:replace_dot</tt> - When the link +name+ isn't provided, the # +email_address+ is used for the link label. You can use this option to # obfuscate the +email_address+ by substituting the . in the email with the # string given as the value. - # * <tt>:subject</tt> - Preset the subject line of the email. + # * <tt>:subject</tt> - Preset the subject line of the email. # * <tt>:body</tt> - Preset the body of the email. - # * <tt>:cc</tt> - Carbon Copy addition recipients on the email. - # * <tt>:bcc</tt> - Blind Carbon Copy additional recipients on the email. + # * <tt>:cc</tt> - Carbon Copy addition recipients on the email. + # * <tt>:bcc</tt> - Blind Carbon Copy additional recipients on the email. # # ==== Examples # mail_to "me@domain.com" @@ -607,23 +606,23 @@ module ActionView submit_function << "f.submit();" end - # Processes the _html_options_ hash, converting the boolean + # Processes the +html_options+ hash, converting the boolean # attributes from true/false form into the form required by # HTML/XHTML. (An attribute is considered to be boolean if - # its name is listed in the given _bool_attrs_ array.) + # its name is listed in the given +bool_attrs+ array.) # - # More specifically, for each boolean attribute in _html_options_ + # More specifically, for each boolean attribute in +html_options+ # given as: # - # "attr" => bool_value + # "attr" => bool_value # - # if the associated _bool_value_ evaluates to true, it is + # if the associated +bool_value+ evaluates to true, it is # replaced with the attribute's name; otherwise the attribute is - # removed from the _html_options_ hash. (See the XHTML 1.0 spec, + # removed from the +html_options+ hash. (See the XHTML 1.0 spec, # section 4.5 "Attribute Minimization" for more: # http://www.w3.org/TR/xhtml1/#h-4.5) # - # Returns the updated _html_options_ hash, which is also modified + # Returns the updated +html_options+ hash, which is also modified # in place. # # Example: diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index 7ffabc210e..63eb5c3eeb 100644 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -33,7 +33,7 @@ rescue LoadError end module ActiveRecord - # TODO: Review explicit loads to see if they will automatically be handled by the initilizer. + # TODO: Review explicit loads to see if they will automatically be handled by the initializer. def self.load_all! [Base, DynamicFinderMatch, ConnectionAdapters::AbstractAdapter] end diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb index af80a579d6..e41fda7a4b 100644 --- a/activerecord/lib/active_record/association_preload.rb +++ b/activerecord/lib/active_record/association_preload.rb @@ -28,7 +28,7 @@ module ActiveRecord # 'books' table is useful; the joined 'authors' data is just redundant, and # processing this redundant data takes memory and CPU time. The problem # quickly becomes worse and worse as the level of eager loading increases - # (i.e. if ActiveRecord is to eager load the associations' assocations as + # (i.e. if ActiveRecord is to eager load the associations' associations as # well). # # The second strategy is to use multiple database queries, one for each @@ -58,7 +58,7 @@ module ActiveRecord # +associations+ specifies one or more associations that you want to # preload. It may be: # - a Symbol or a String which specifies a single association name. For - # example, specifiying +:books+ allows this method to preload all books + # example, specifying +:books+ allows this method to preload all books # for an Author. # - an Array which specifies multiple association names. This array # is processed recursively. For example, specifying <tt>[:avatar, :books]</tt> diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 934beb7d39..66aa9332c8 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -519,13 +519,13 @@ module ActiveRecord # # Post.find(:all, :include => [ :author, :comments ], :conditions => ['comments.approved = ?', true]) # - # will result in a single SQL query with joins along the lines of: <tt>LEFT OUTER JOIN comments ON comments.post_id = posts.id</tt> and + # This will result in a single SQL query with joins along the lines of: <tt>LEFT OUTER JOIN comments ON comments.post_id = posts.id</tt> and # <tt>LEFT OUTER JOIN authors ON authors.id = posts.author_id</tt>. Note that using conditions like this can have unintended consequences. # In the above example posts with no approved comments are not returned at all, because the conditions apply to the SQL statement as a whole # and not just to the association. You must disambiguate column references for this fallback to happen, for example # <tt>:order => "author.name DESC"</tt> will work but <tt>:order => "name DESC"</tt> will not. # - # If you do want eagerload only some members of an association it is usually more natural to <tt>:include</tt> an association + # If you do want eager load only some members of an association it is usually more natural to <tt>:include</tt> an association # which has conditions defined on it: # # class Post < ActiveRecord::Base @@ -534,7 +534,7 @@ module ActiveRecord # # Post.find(:all, :include => :approved_comments) # - # will load posts and eager load the +approved_comments+ association, which contains only those comments that have been approved. + # This will load posts and eager load the +approved_comments+ association, which contains only those comments that have been approved. # # If you eager load an association with a specified <tt>:limit</tt> option, it will be ignored, returning all the associated objects: # @@ -557,7 +557,7 @@ module ActiveRecord # # Address.find(:all, :include => :addressable) # - # will execute one query to load the addresses and load the addressables with one query per addressable type. + # This will execute one query to load the addresses and load the addressables with one query per addressable type. # For example if all the addressables are either of class Person or Company then a total of 3 queries will be executed. The list of # addressable types to load is determined on the back of the addresses loaded. This is not supported if Active Record has to fallback # to the previous implementation of eager loading and will raise ActiveRecord::EagerLoadPolymorphicError. The reason is that the parent @@ -641,6 +641,60 @@ module ActiveRecord # end # end # + # == Bi-directional associations + # + # When you specify an association there is usually an association on the associated model that specifies the same + # relationship in reverse. For example, with the following models: + # + # class Dungeon < ActiveRecord::Base + # has_many :traps + # has_one :evil_wizard + # end + # + # class Trap < ActiveRecord::Base + # belongs_to :dungeon + # end + # + # class EvilWizard < ActiveRecord::Base + # belongs_to :dungeon + # end + # + # The +traps+ association on +Dungeon+ and the the +dungeon+ association on +Trap+ are the inverse of each other and the + # inverse of the +dungeon+ association on +EvilWizard+ is the +evil_wizard+ association on +Dungeon+ (and vice-versa). By default, + # +ActiveRecord+ doesn't do know anything about these inverse relationships and so no object loading optimisation is possible. For example: + # + # d = Dungeon.first + # t = d.traps.first + # d.level == t.dungeon.level # => true + # d.level = 10 + # d.level == t.dungeon.level # => false + # + # The +Dungeon+ instances +d+ and <tt>t.dungeon</tt> in the above example refer to the same object data from the database, but are + # actually different in-memory copies of that data. Specifying the <tt>:inverse_of</tt> option on associations lets you tell + # +ActiveRecord+ about inverse relationships and it will optimise object loading. For example, if we changed our model definitions to: + # + # class Dungeon < ActiveRecord::Base + # has_many :traps, :inverse_of => :dungeon + # has_one :evil_wizard, :inverse_of => :dungeon + # end + # + # class Trap < ActiveRecord::Base + # belongs_to :dungeon, :inverse_of => :traps + # end + # + # class EvilWizard < ActiveRecord::Base + # belongs_to :dungeon, :inverse_of => :evil_wizard + # end + # + # Then, from our code snippet above, +d+ and <tt>t.dungeon</tt> are actually the same in-memory instance and our final <tt>d.level == t.dungeon.level</tt> + # will return +true+. + # + # There are limitations to <tt>:inverse_of</tt> support: + # + # * does not work with <tt>:through</tt> associations. + # * does not work with <tt>:polymorphic</tt> associations. + # * for +belongs_to+ associations +has_many+ inverse associations are ignored. + # # == Type safety with <tt>ActiveRecord::AssociationTypeMismatch</tt> # # If you attempt to assign an object to an association that doesn't match the inferred or specified <tt>:class_name</tt>, you'll @@ -781,6 +835,10 @@ module ActiveRecord # If false, don't validate the associated objects when saving the parent object. true by default. # [:autosave] # If true, always save any loaded members and destroy members marked for destruction, when saving the parent object. Off by default. + # [:inverse_of] + # Specifies the name of the <tt>belongs_to</tt> association on the associated object that is the inverse of this <tt>has_many</tt> + # association. Does not work in combination with <tt>:through</tt> or <tt>:as</tt> options. + # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional assocations for more detail. # # Option examples: # has_many :comments, :order => "posted_on" @@ -890,6 +948,10 @@ module ActiveRecord # If false, don't validate the associated object when saving the parent object. +false+ by default. # [:autosave] # If true, always save the associated object or destroy it if marked for destruction, when saving the parent object. Off by default. + # [:inverse_of] + # Specifies the name of the <tt>belongs_to</tt> association on the associated object that is the inverse of this <tt>has_one</tt> + # association. Does not work in combination with <tt>:through</tt> or <tt>:as</tt> options. + # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional assocations for more detail. # # Option examples: # has_one :credit_card, :dependent => :destroy # destroys the associated credit card @@ -992,6 +1054,10 @@ module ActiveRecord # [:touch] # If true, the associated object will be touched (the updated_at/on attributes set to now) when this record is either saved or # destroyed. If you specify a symbol, that attribute will be updated with the current time instead of the updated_at/on attribute. + # [:inverse_of] + # Specifies the name of the <tt>has_one</tt> or <tt>has_many</tt> association on the associated object that is the inverse of this <tt>belongs_to</tt> + # association. Does not work in combination with the <tt>:polymorphic</tt> options. + # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional assocations for more detail. # # Option examples: # belongs_to :firm, :foreign_key => "client_of" @@ -1201,7 +1267,7 @@ module ActiveRecord private # Generates a join table name from two provided table names. - # The names in the join table namesme end up in lexicographic order. + # The names in the join table names end up in lexicographic order. # # join_table_name("members", "clubs") # => "clubs_members" # join_table_name("members", "special_clubs") # => "members_special_clubs" diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index 84edaec15e..e67ccfb228 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -11,7 +11,7 @@ module ActiveRecord # ones created with +build+ are added to the target. So, the target may be # non-empty and still lack children waiting to be read from the database. # If you look directly to the database you cannot assume that's the entire - # collection because new records may have beed added to the target, etc. + # collection because new records may have been added to the target, etc. # # If you need to work on all current children, new and existing records, # +load_target+ and the +loaded+ flag are your friends. @@ -228,7 +228,7 @@ module ActiveRecord self end - # Destory all the records from this association. + # Destroy all the records from this association. # # See destroy for more info. def destroy_all diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 62e97158ec..5a36ff5ba2 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -256,6 +256,12 @@ module ActiveRecord #:nodoc: # # Student.find(:all, :conditions => { :grade => [9,11,12] }) # + # When joining tables, nested hashes or keys written in the form 'table_name.column_name' can be used to qualify the table name of a + # particular condition. For instance: + # + # Student.find(:all, :conditions => { :schools => { :type => 'public' }}, :joins => :schools) + # Student.find(:all, :conditions => { 'schools.type' => 'public' }, :joins => :schools) + # # == Overwriting default accessors # # All column values are automatically available through basic accessors on the Active Record object, but sometimes you @@ -854,7 +860,7 @@ module ActiveRecord #:nodoc: # Book.update_all "author = 'David'", "title LIKE '%Rails%'" # # # Update all avatars migrated more than a week ago - # Avatar.update_all ['migrated_at = ?, Time.now.utc], ['migrated_at > ?', 1.week.ago] + # Avatar.update_all ['migrated_at = ?', Time.now.utc], ['migrated_at > ?', 1.week.ago] # # # Update all books that match our conditions, but limit it to 5 ordered by date # Book.update_all "author = 'David'", "title LIKE '%Rails%'", :order => 'created_at', :limit => 5 @@ -1055,6 +1061,21 @@ module ActiveRecord #:nodoc: # # To start from an all-closed default and enable attributes as needed, # have a look at +attr_accessible+. + # + # If the access logic of your application is richer you can use <tt>Hash#except</tt> + # or <tt>Hash#slice</tt> to sanitize the hash of parameters before they are + # passed to Active Record. + # + # For example, it could be the case that the list of protected attributes + # for a given model depends on the role of the user: + # + # # Assumes plan_id is not protected because it depends on the role. + # params[:account] = params[:account].except(:plan_id) unless admin? + # @account.update_attributes(params[:account]) + # + # Note that +attr_protected+ is still applied to the received hash. Thus, + # with this technique you can at most _extend_ the list of protected + # attributes for a particular mass-assignment call. def attr_protected(*attributes) write_inheritable_attribute(:attr_protected, Set.new(attributes.map {|a| a.to_s}) + (protected_attributes || [])) end @@ -1088,6 +1109,21 @@ module ActiveRecord #:nodoc: # # customer.credit_rating = "Average" # customer.credit_rating # => "Average" + # + # If the access logic of your application is richer you can use <tt>Hash#except</tt> + # or <tt>Hash#slice</tt> to sanitize the hash of parameters before they are + # passed to Active Record. + # + # For example, it could be the case that the list of accessible attributes + # for a given model depends on the role of the user: + # + # # Assumes plan_id is accessible because it depends on the role. + # params[:account] = params[:account].except(:plan_id) unless admin? + # @account.update_attributes(params[:account]) + # + # Note that +attr_accessible+ is still applied to the received hash. Thus, + # with this technique you can at most _narrow_ the list of accessible + # attributes for a particular mass-assignment call. def attr_accessible(*attributes) write_inheritable_attribute(:attr_accessible, Set.new(attributes.map(&:to_s)) + (accessible_attributes || [])) end @@ -1382,14 +1418,14 @@ module ActiveRecord #:nodoc: classes rescue # OPTIMIZE this rescue is to fix this test: ./test/cases/reflection_test.rb:56:in `test_human_name_for_column' - # Appearantly the method base_class causes some trouble. + # Apparently the method base_class causes some trouble. # It now works for sure. [self] end # Transforms attribute key names into a more humane format, such as "First name" instead of "first_name". Example: # Person.human_attribute_name("first_name") # => "First name" - # This used to be depricated in favor of humanize, but is now preferred, because it automatically uses the I18n + # This used to be deprecated in favor of humanize, but is now preferred, because it automatically uses the I18n # module now. # Specify +options+ with additional translating options. def human_attribute_name(attribute_key_name, options = {}) diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb index 3aa0b8f1b5..4a2ec5bf95 100644 --- a/activerecord/lib/active_record/callbacks.rb +++ b/activerecord/lib/active_record/callbacks.rb @@ -22,7 +22,7 @@ module ActiveRecord # * (8) <tt>after_save</tt> # # That's a total of eight callbacks, which gives you immense power to react and prepare for each state in the - # Active Record lifecycle. The sequence for calling <tt>Base#save</tt> an existing record is similar, except that each + # Active Record lifecycle. The sequence for calling <tt>Base#save</tt> for an existing record is similar, except that each # <tt>_on_create</tt> callback is replaced by the corresponding <tt>_on_update</tt> callback. # # Examples: @@ -262,6 +262,10 @@ module ActiveRecord # Is called _after_ <tt>Base.save</tt> on new objects that haven't been saved yet (no record exists). # Note that this callback is still wrapped in the transaction around +save+. For example, if you # invoke an external indexer at this point it won't see the changes in the database. + # + # class Contact < ActiveRecord::Base + # after_create { |record| logger.info( "Contact #{record.id} was created." ) } + # end def after_create() end def create_with_callbacks #:nodoc: return false if callback(:before_create) == false @@ -272,11 +276,19 @@ module ActiveRecord private :create_with_callbacks # Is called _before_ <tt>Base.save</tt> on existing objects that have a record. + # + # class Contact < ActiveRecord::Base + # before_update { |record| logger.info( "Contact #{record.id} is about to be updated." ) } + # end def before_update() end # Is called _after_ <tt>Base.save</tt> on existing objects that have a record. # Note that this callback is still wrapped in the transaction around +save+. For example, if you # invoke an external indexer at this point it won't see the changes in the database. + # + # class Contact < ActiveRecord::Base + # after_update { |record| logger.info( "Contact #{record.id} was updated." ) } + # end def after_update() end def update_with_callbacks(*args) #:nodoc: @@ -326,6 +338,10 @@ module ActiveRecord # # Note: If you need to _destroy_ or _nullify_ associated records first, # use the <tt>:dependent</tt> option on your associations. + # + # class Contact < ActiveRecord::Base + # after_destroy { |record| logger.info( "Contact #{record.id} is about to be destroyed." ) } + # end def before_destroy() end # Is called _after_ <tt>Base.destroy</tt> (and all the attributes have been frozen). diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb index 500dafdc2e..12253eac3f 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -362,7 +362,7 @@ module ActiveRecord def call(env) @app.call(env) ensure - # Don't return connection (and peform implicit rollback) if + # Don't return connection (and perform implicit rollback) if # this request is a part of integration test unless env.key?("rack.test") ActiveRecord::Base.clear_active_connections! diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index f44cd0bd5a..2473c772e3 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -41,11 +41,19 @@ module ActiveRecord # # create_table() passes a TableDefinition object to the block. # # This form will not only create the table, but also columns for the # # table. + # # create_table(:suppliers) do |t| # t.column :name, :string, :limit => 60 # # Other fields here # end # + # === Block form, with shorthand + # # You can also use the column types as method calls, rather than calling the column method. + # create_table(:suppliers) do |t| + # t.string :name, :limit => 60 + # # Other fields here + # end + # # === Regular form # # Creates a table called 'suppliers' with no columns. # create_table(:suppliers) diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index 2b0cfc2c3b..6eeeddc9e1 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -409,7 +409,7 @@ end # subdomain: $LABEL # # Also, sometimes (like when porting older join table fixtures) you'll need -# to be able to get ahold of the identifier for a given label. ERB +# to be able to get a hold of the identifier for a given label. ERB # to the rescue: # # george_reginald: diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 467d955a49..3963baa6b8 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -109,8 +109,8 @@ module ActiveRecord # script/generate migration MyNewMigration # # where MyNewMigration is the name of your migration. The generator will - # create an empty migration file <tt>nnn_my_new_migration.rb</tt> in the <tt>db/migrate/</tt> - # directory where <tt>nnn</tt> is the next largest migration number. + # create an empty migration file <tt>timestamp_my_new_migration.rb</tt> in the <tt>db/migrate/</tt> + # directory where <tt>timestamp</tt> is the UTC formatted date and time that the migration was generated. # # You may then edit the <tt>self.up</tt> and <tt>self.down</tt> methods of # MyNewMigration. @@ -118,7 +118,7 @@ module ActiveRecord # There is a special syntactic shortcut to generate migrations that add fields to a table. # script/generate migration add_fieldname_to_tablename fieldname:string # - # This will generate the file <tt>nnn_add_fieldname_to_tablename</tt>, which will look like this: + # This will generate the file <tt>timestamp_add_fieldname_to_tablename</tt>, which will look like this: # class AddFieldnameToTablename < ActiveRecord::Migration # def self.up # add_column :tablenames, :fieldname, :string diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index 0beb4321a2..bc4cca7855 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -284,7 +284,7 @@ module ActiveRecord # }) # # Will update the name of the Person with ID 1, build a new associated - # person with the name `John', and mark the associatied Person with ID 2 + # person with the name `John', and mark the associated Person with ID 2 # for destruction. # # Also accepts an Array of attribute hashes: diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index 7ac6f6fe3b..a7fa98756e 100644 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -63,7 +63,7 @@ module ActiveRecord # default message (e.g. <tt>activerecord.errors.messages.MESSAGE</tt>). The translated model name, # translated attribute name and the value are available for interpolation. # - # When using inheritence in your models, it will check all the inherited models too, but only if the model itself + # When using inheritance in your models, it will check all the inherited models too, but only if the model itself # hasn't been found. Say you have <tt>class Admin < User; end</tt> and you wanted the translation for the <tt>:blank</tt> # error +message+ for the <tt>title</tt> +attribute+, it looks for these translations: # diff --git a/activesupport/lib/active_support/buffered_logger.rb b/activesupport/lib/active_support/buffered_logger.rb index ee66479dde..dfce507b33 100644 --- a/activesupport/lib/active_support/buffered_logger.rb +++ b/activesupport/lib/active_support/buffered_logger.rb @@ -68,6 +68,10 @@ module ActiveSupport message end + # Dynamically add methods such as: + # def info + # def warn + # def debug for severity in Severity.constants class_eval <<-EOT, __FILE__, __LINE__ + 1 def #{severity.downcase}(message = nil, progname = nil, &block) # def debug(message = nil, progname = nil, &block) diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb index 954d0f5423..96a8000778 100644 --- a/activesupport/lib/active_support/cache/mem_cache_store.rb +++ b/activesupport/lib/active_support/cache/mem_cache_store.rb @@ -12,7 +12,7 @@ module ActiveSupport # and MemCacheStore will load balance between all available servers. If a # server goes down, then MemCacheStore will ignore it until it goes back # online. - # - Time-based expiry support. See #write and the +:expires_in+ option. + # - Time-based expiry support. See #write and the <tt>:expires_in</tt> option. # - Per-request in memory cache for all communication with the MemCache server(s). class MemCacheStore < Store module Response # :nodoc: @@ -64,9 +64,9 @@ module ActiveSupport # Writes a value to the cache. # # Possible options: - # - +:unless_exist+ - set to true if you don't want to update the cache + # - <tt>:unless_exist</tt> - set to true if you don't want to update the cache # if the key is already set. - # - +:expires_in+ - the number of seconds that this value may stay in + # - <tt>:expires_in</tt> - the number of seconds that this value may stay in # the cache. See ActiveSupport::Cache::Store#write for an example. def write(key, value, options = nil) super diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb index f049189b9a..238d1b6804 100644 --- a/activesupport/lib/active_support/callbacks.rb +++ b/activesupport/lib/active_support/callbacks.rb @@ -237,7 +237,7 @@ module ActiveSupport # * the result from the callback # * the object which has the callback # - # If the result from the block evaluates to false, the callback chain is stopped. + # If the result from the block evaluates to +true+, the callback chain is stopped. # # Example: # class Storage diff --git a/activesupport/lib/active_support/core_ext/array/conversions.rb b/activesupport/lib/active_support/core_ext/array/conversions.rb index 5f1ce4142f..11846f265c 100644 --- a/activesupport/lib/active_support/core_ext/array/conversions.rb +++ b/activesupport/lib/active_support/core_ext/array/conversions.rb @@ -12,7 +12,7 @@ class Array default_two_words_connector = I18n.translate(:'support.array.two_words_connector', :locale => options[:locale]) default_last_word_connector = I18n.translate(:'support.array.last_word_connector', :locale => options[:locale]) - # Try to emulate to_senteces previous to 2.3 + # Try to emulate to_sentences previous to 2.3 if options.has_key?(:connector) || options.has_key?(:skip_last_comma) ::ActiveSupport::Deprecation.warn(":connector has been deprecated. Use :words_connector instead", caller) if options.has_key? :connector ::ActiveSupport::Deprecation.warn(":skip_last_comma has been deprecated. Use :last_word_connector instead", caller) if options.has_key? :skip_last_comma diff --git a/activesupport/lib/active_support/core_ext/enumerable.rb b/activesupport/lib/active_support/core_ext/enumerable.rb index 8309b617ae..434a32b29b 100644 --- a/activesupport/lib/active_support/core_ext/enumerable.rb +++ b/activesupport/lib/active_support/core_ext/enumerable.rb @@ -42,7 +42,7 @@ module Enumerable # # The latter is a shortcut for: # - # payments.inject { |sum, p| sum + p.price } + # payments.inject(0) { |sum, p| sum + p.price } # # It can also calculate the sum without the use of a block. # diff --git a/activesupport/lib/active_support/core_ext/object/duplicable.rb b/activesupport/lib/active_support/core_ext/object/duplicable.rb index 8f49ddfd9e..1722726ca2 100644 --- a/activesupport/lib/active_support/core_ext/object/duplicable.rb +++ b/activesupport/lib/active_support/core_ext/object/duplicable.rb @@ -1,6 +1,6 @@ class Object # Can you safely .dup this object? - # False for nil, false, true, symbols, and numbers; true otherwise. + # False for nil, false, true, symbols, numbers, and class objects; true otherwise. def duplicable? true end diff --git a/activesupport/lib/active_support/core_ext/time/marshal_with_utc_flag.rb b/activesupport/lib/active_support/core_ext/time/marshal_with_utc_flag.rb index a1c8ece1d7..9de8157eb0 100644 --- a/activesupport/lib/active_support/core_ext/time/marshal_with_utc_flag.rb +++ b/activesupport/lib/active_support/core_ext/time/marshal_with_utc_flag.rb @@ -1,5 +1,5 @@ # Pre-1.9 versions of Ruby have a bug with marshaling Time instances, where utc instances are -# unmarshaled in the local zone, instead of utc. We're layering behavior on the _dump and _load +# unmarshalled in the local zone, instead of utc. We're layering behavior on the _dump and _load # methods so that utc instances can be flagged on dump, and coerced back to utc on load. if RUBY_VERSION < '1.9' class Time diff --git a/activesupport/lib/active_support/inflector.rb b/activesupport/lib/active_support/inflector.rb index 92c1de057b..4ee96b13b4 100644 --- a/activesupport/lib/active_support/inflector.rb +++ b/activesupport/lib/active_support/inflector.rb @@ -155,7 +155,7 @@ module ActiveSupport # Examples: # "posts".singularize # => "post" # "octopi".singularize # => "octopus" - # "sheep".singluarize # => "sheep" + # "sheep".singularize # => "sheep" # "word".singularize # => "word" # "CamelOctopi".singularize # => "CamelOctopus" def singularize(word) @@ -261,9 +261,9 @@ module ActiveSupport # <%= link_to(@person.name, person_path(@person)) %> # # => <a href="/person/1-donald-e-knuth">Donald E. Knuth</a> def parameterize(string, sep = '-') - # replace accented chars with ther ascii equivalents + # replace accented chars with their ascii equivalents parameterized_string = transliterate(string) - # Turn unwanted chars into the seperator + # Turn unwanted chars into the separator parameterized_string.gsub!(/[^a-z0-9\-_\+]+/i, sep) unless sep.blank? re_sep = Regexp.escape(sep) diff --git a/activesupport/lib/active_support/xml_mini/jdom.rb b/activesupport/lib/active_support/xml_mini/jdom.rb index 1cd714d864..5c3d93c4a1 100644 --- a/activesupport/lib/active_support/xml_mini/jdom.rb +++ b/activesupport/lib/active_support/xml_mini/jdom.rb @@ -78,7 +78,7 @@ module ActiveSupport # Merge all the texts of an element into the hash # # hash:: - # Hash to add the converted emement to. + # Hash to add the converted element to. # element:: # XML element whose texts are to me merged into the hash def merge_texts!(hash, element) diff --git a/activesupport/lib/active_support/xml_mini/nokogiri.rb b/activesupport/lib/active_support/xml_mini/nokogiri.rb index 622523a1b9..847ab0152b 100644 --- a/activesupport/lib/active_support/xml_mini/nokogiri.rb +++ b/activesupport/lib/active_support/xml_mini/nokogiri.rb @@ -59,7 +59,7 @@ module ActiveSupport memo[name] = child_hash end - # Recusively walk children + # Recursively walk children child.children.each { |c| callback.call(child_hash, child, c, callback) } diff --git a/activesupport/lib/active_support/xml_mini/rexml.rb b/activesupport/lib/active_support/xml_mini/rexml.rb index aa2461535b..aaf4bb6bfd 100644 --- a/activesupport/lib/active_support/xml_mini/rexml.rb +++ b/activesupport/lib/active_support/xml_mini/rexml.rb @@ -60,7 +60,7 @@ module ActiveSupport # Merge all the texts of an element into the hash # # hash:: - # Hash to add the converted emement to. + # Hash to add the converted element to. # element:: # XML element whose texts are to me merged into the hash def merge_texts!(hash, element) diff --git a/railties/README b/railties/README index 37ec8ea211..221e8486d1 100644 --- a/railties/README +++ b/railties/README @@ -28,7 +28,7 @@ link:files/vendor/rails/actionpack/README.html. == Getting Started 1. At the command prompt, start a new Rails application using the <tt>rails</tt> command - and your application name. Ex: rails myapp + and your application name. Ex: <tt>rails myapp</tt> 2. Change directory into myapp and start the web server: <tt>script/server</tt> (run with --help for options) 3. Go to http://localhost:3000/ and get "Welcome aboard: You're riding the Rails!" 4. Follow the guidelines to start developing your application @@ -36,61 +36,60 @@ link:files/vendor/rails/actionpack/README.html. == Web Servers -By default, Rails will try to use Mongrel if it's are installed when started with script/server, otherwise Rails will use WEBrick, the webserver that ships with Ruby. But you can also use Rails -with a variety of other web servers. +By default, Rails will try to use Mongrel if it's installed when started with script/server, otherwise +Rails will use WEBrick, the webserver that ships with Ruby. But you can also use Rails with a variety of +other web servers. Mongrel is a Ruby-based webserver with a C component (which requires compilation) that is suitable for development and deployment of Rails applications. If you have Ruby Gems installed, getting up and running with mongrel is as easy as: <tt>gem install mongrel</tt>. More info at: http://mongrel.rubyforge.org -Say other Ruby web servers like Thin and Ebb or regular web servers like Apache or LiteSpeed or -Lighttpd or IIS. The Ruby web servers are run through Rack and the latter can either be setup to use -FCGI or proxy to a pack of Mongrels/Thin/Ebb servers. +Other ruby web servers exist which can run your rails application, however script/server does +not search for them or start them. These include Thin, Ebb, and Apache with mod_rails. + +For production use, often a web/proxy server such as Apache, LiteSpeed, Lighttpd or IIS is +deployed as the front-end server, with the chosen ruby web server running in the back-end +and receiving the proxied requests via one of several protocols (HTTP, CGI, FCGI). + == Apache .htaccess example for FCGI/CGI -# General Apache options -AddHandler fastcgi-script .fcgi -AddHandler cgi-script .cgi -Options +FollowSymLinks +ExecCGI - -# If you don't want Rails to look in certain directories, -# use the following rewrite rules so that Apache won't rewrite certain requests -# -# Example: -# RewriteCond %{REQUEST_URI} ^/notrails.* -# RewriteRule .* - [L] - -# Redirect all requests not available on the filesystem to Rails -# By default the cgi dispatcher is used which is very slow -# -# For better performance replace the dispatcher with the fastcgi one -# -# Example: -# RewriteRule ^(.*)$ dispatch.fcgi [QSA,L] -RewriteEngine On - -# If your Rails application is accessed via an Alias directive, -# then you MUST also set the RewriteBase in this htaccess file. -# -# Example: -# Alias /myrailsapp /path/to/myrailsapp/public -# RewriteBase /myrailsapp - -RewriteRule ^$ index.html [QSA] -RewriteRule ^([^.]+)$ $1.html [QSA] -RewriteCond %{REQUEST_FILENAME} !-f -RewriteRule ^(.*)$ dispatch.cgi [QSA,L] - -# In case Rails experiences terminal errors -# Instead of displaying this message you can supply a file here which will be rendered instead -# -# Example: -# ErrorDocument 500 /500.html - -ErrorDocument 500 "<h2>Application error</h2>Rails application failed to start properly" +General Apache options + + AddHandler fastcgi-script .fcgi + AddHandler cgi-script .cgi + Options +FollowSymLinks +ExecCGI + +If you don't want Rails to look in certain directories, use the following +rewrite rules so that Apache won't rewrite certain requests. + + RewriteCond %{REQUEST_URI} ^/notrails.* + RewriteRule .* - [L] + +Redirect all requests not available on the filesystem to Rails. By default the +cgi dispatcher is used which is very slow, for better performance replace the +dispatcher with the fastcgi one. + + RewriteRule ^(.*)$ dispatch.fcgi [QSA,L] + RewriteEngine On + +If your Rails application is accessed via an Alias directive, then you MUST also +set the RewriteBase in this htaccess file. + + Alias /myrailsapp /path/to/myrailsapp/public + RewriteBase /myrailsapp + + RewriteRule ^$ index.html [QSA] + RewriteRule ^([^.]+)$ $1.html [QSA] + RewriteCond %{REQUEST_FILENAME} !-f + RewriteRule ^(.*)$ dispatch.cgi [QSA,L] + +Incase Rails experiences terminal errors instead of displaying those messages you +can supply a file here which will be rendered instead. + ErrorDocument 500 /500.html + ErrorDocument 500 "<h2>Application error</h2>Rails application failed to start properly" == Debugging Rails diff --git a/railties/configs/seeds.rb b/railties/configs/seeds.rb index 3174d0cb8a..bc8695e6f0 100644 --- a/railties/configs/seeds.rb +++ b/railties/configs/seeds.rb @@ -4,4 +4,4 @@ # Examples: # # cities = City.create([{ :name => 'Chicago' }, { :name => 'Copenhagen' }]) -# Major.create(:name => 'Daley', :city => cities.first) +# Mayor.create(:name => 'Daley', :city => cities.first) diff --git a/railties/guides/files/javascripts/guides.js b/railties/guides/files/javascripts/guides.js index 81fc07e799..c4e4d459ea 100755 --- a/railties/guides/files/javascripts/guides.js +++ b/railties/guides/files/javascripts/guides.js @@ -1,5 +1,4 @@ function guideMenu(){ - if (document.getElementById('guides').style.display == "none") { document.getElementById('guides').style.display = "block"; } else { diff --git a/railties/guides/images/fxn.jpg b/railties/guides/images/fxn.jpg Binary files differdeleted file mode 100644 index 81999341f1..0000000000 --- a/railties/guides/images/fxn.jpg +++ /dev/null diff --git a/railties/guides/images/fxn.png b/railties/guides/images/fxn.png Binary files differnew file mode 100644 index 0000000000..9b531ee584 --- /dev/null +++ b/railties/guides/images/fxn.png diff --git a/railties/guides/source/action_controller_overview.textile b/railties/guides/source/action_controller_overview.textile index 054ca99985..756caea5fe 100644 --- a/railties/guides/source/action_controller_overview.textile +++ b/railties/guides/source/action_controller_overview.textile @@ -41,7 +41,7 @@ def new end </ruby> -The "Layouts & rendering guide":layouts_and_rendering.html explains this in more detail. +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. @@ -191,7 +191,7 @@ Session values are stored using key/value pairs like a hash: <ruby> class ApplicationController < ActionController::Base -private + private # Finds the User with the ID stored in the session with the key # :current_user_id This is a common way to handle user login in @@ -350,7 +350,8 @@ Before filters may halt the request cycle. A common before filter is one which r class ApplicationController < ActionController::Base before_filter :require_login -private + private + def require_login unless logged_in? flash[:error] = "You must be logged in to access this section" @@ -374,7 +375,7 @@ The method simply stores an error message in the flash and redirects to the logi In this example the filter is added to +ApplicationController+ and thus all controllers in the application inherit it. This will make everything in the application require the user to be logged in in order to use it. For obvious reasons (the user wouldn't be able to log in in the first place!), not all controllers or actions should require this. You can prevent this filter from running before particular actions with +skip_before_filter+: <ruby> -class LoginsController < Application +class LoginsController < ApplicationController skip_before_filter :require_login, :only => [:new, :create] end </ruby> @@ -390,10 +391,11 @@ Around filters are responsible for running the action, but they can choose not t <ruby> # Example taken from the Rails API filter documentation: # http://api.rubyonrails.org/classes/ActionController/Filters/ClassMethods.html -class ApplicationController < Application +class ApplicationController < ActionController::Base around_filter :catch_exceptions -private + private + def catch_exceptions yield rescue => exception @@ -442,7 +444,7 @@ The Rails API documentation has "more information on using filters":http://api.r h3. Verification -Verifications make sure certain criteria are met in order for a controller or action to run. They can specify that a certain key (or several keys in the form of an array) is present in the +params+, +session+ or +flash+ hashes or that a certain HTTP method was used or that the request was made using +XMLHTTPRequest+ (Ajax). The default action taken when these criteria are not met is to render a 400 Bad Request response, but you can customize this by specifying a redirect URL or rendering something else and you can also add flash messages and HTTP headers to the response. It is described in the "API documentation":http://api.rubyonrails.org/classes/ActionController/Verification/ClassMethods.html as "essentially a special kind of before_filter". +Verifications make sure certain criteria are met in order for a controller or action to run. They can specify that a certain key (or several keys in the form of an array) is present in the +params+, +session+ or +flash+ hashes or that a certain HTTP method was used or that the request was made using +XMLHttpRequest+ (Ajax). The default action taken when these criteria are not met is to render a 400 Bad Request response, but you can customize this by specifying a redirect URL or rendering something else and you can also add flash messages and HTTP headers to the response. It is described in the "API documentation":http://api.rubyonrails.org/classes/ActionController/Verification/ClassMethods.html as "essentially a special kind of before_filter". Here's an example of using verification to make sure the user supplies a username and a password in order to log in: @@ -575,7 +577,8 @@ class AdminController < ApplicationController before_filter :authenticate -private + private + def authenticate authenticate_or_request_with_http_basic do |username, password| username == USERNAME && @@ -597,7 +600,8 @@ class AdminController < ApplicationController before_filter :authenticate -private + private + def authenticate authenticate_or_request_with_http_digest do |username| USERS[username] @@ -626,7 +630,7 @@ class ClientsController < ApplicationController :type => "application/pdf") end -private + private def generate_pdf(client) Prawn::Document.new do @@ -728,7 +732,8 @@ Here's how you can use +rescue_from+ to intercept all +ActiveRecord::RecordNotFo class ApplicationController < ActionController::Base rescue_from ActiveRecord::RecordNotFound, :with => :record_not_found -private + private + def record_not_found render :text => "404 Not Found", :status => 404 end @@ -741,7 +746,8 @@ Of course, this example is anything but elaborate and doesn't improve on the def class ApplicationController < ActionController::Base rescue_from User::NotAuthorized, :with => :user_not_authorized -private + private + def user_not_authorized flash[:error] = "You don't have access to this section." redirect_to :back @@ -757,7 +763,8 @@ class ClientsController < ApplicationController @client = Client.find(params[:id]) end -private + private + # If the user is not authorized, just throw the exception. def check_authorization raise User::NotAuthorized unless current_user.admin? diff --git a/railties/guides/source/action_mailer_basics.textile b/railties/guides/source/action_mailer_basics.textile index 9476635ae6..2e7f0e7fed 100644 --- a/railties/guides/source/action_mailer_basics.textile +++ b/railties/guides/source/action_mailer_basics.textile @@ -46,7 +46,7 @@ class UserMailer < ActionMailer::Base from "My Awesome Site Notifications <notifications@example.com>" subject "Welcome to My Awesome Site" sent_on Time.now - body {:user => user, :url => "http://example.com/login"} + body( {:user => user, :url => "http://example.com/login"}) end end </ruby> @@ -137,9 +137,9 @@ Hence, if the method name starts with +deliver_+ followed by any combination of h4. Complete List of Action Mailer User-Settable Attributes -|bcc| The BCC addresses of the email| +|bcc| The BCC addresses of the email, either as a string (for a single address) or an array of strings (for multiple addresses)| |body| The body of the email. This is either a hash (in which case it specifies the variables to pass to the template when it is rendered), or a string, in which case it specifies the actual body of the message| -|cc| The CC addresses for the email| +|cc| The CC addresses for the email, either as a string (for a single address) or an array of strings (for multiple addresses)| |charset| The charset to use for the email. This defaults to the +default_charset+ specified for ActionMailer::Base.| |content_type| The content type for the email. This defaults to "text/plain" but the filename may specify it| |from| The from address of the email| @@ -165,10 +165,11 @@ class UserMailer < ActionMailer::Base from "My Awesome Site Notifications<notifications@example.com>" subject "Welcome to My Awesome Site" sent_on Time.now - body {:user => user, :url => "http://example.com/login"} + body( {:user => user, :url => "http://example.com/login"}) content_type "text/html" # use some_other_template.text.(html|plain).erb instead template "some_other_template" + end end </ruby> @@ -264,9 +265,9 @@ end h4. Sending Multipart Emails with Attachments -Once you use the +attachment+ method, ActionMailer will no longer automagically use the correct template based on the filename. You must declare which template you are using for each content type via the +part+ method. +Once you use the +attachment+ method, ActionMailer will no longer automagically use the correct template based on the filename, nor will it properly order the alternative parts. You must declare which template you are using for each content type via the +part+ method. And you must declare these templates in the proper order. -In the following example, there would be two template files, +welcome_email_html.erb+ and +welcome_email_plain.erb+ in the +app/views/user_mailer+ folder. +In the following example, there would be two template files, +welcome_email_html.erb+ and +welcome_email_plain.erb+ in the +app/views/user_mailer+ folder. The +text/plain+ part must be listed first for full compatibility with email clients. If +text/plain+ is listed after +text/html+, some clients may display both the HTML and plain text versions of the email. The text alternatives alone must be enclosed in a +multipart/alternative+ part. Do not set the entire message's +content_type+ to +multipart/alternative+ or some email clients may ignore the display of attachments such as PDF's. <ruby> class UserMailer < ActionMailer::Base @@ -274,14 +275,15 @@ class UserMailer < ActionMailer::Base recipients user.email_address subject "New account information" from "system@example.com" - content_type "multipart/alternative" - part "text/html" do |p| - p.body = render_message("welcome_email_html", :message => "<h1>HTML content</h1>") - end + part "multipart/alternative" do |pt| + pt.part "text/plain" do |p| + p.body = render_message("welcome_email_plain", :message => "text content") + end - part "text/plain" do |p| - p.body = render_message("welcome_email_plain", :message => "text content") + pt.part "text/html" do |p| + p.body = render_message("welcome_email_html", :message => "<h1>HTML content</h1>") + end end attachment :content_type => "image/jpeg", @@ -369,9 +371,9 @@ ActionMailer::Base.default_charset = "iso-8859-1" h4. Action Mailer Configuration for GMail -Instructions copied from http://http://www.fromjavatoruby.com/2008/11/actionmailer-with-gmail-must-issue.html +Instructions copied from "this blog entry":http://www.fromjavatoruby.com/2008/11/actionmailer-with-gmail-must-issue.html by Robert Zotter. -First you must install the +action_mailer_tls+ plugin from http://code.openrain.com/rails/action_mailer_tls/, then all you have to do is configure action mailer. +First you must install the "action_mailer_tls":http://github.com/openrain/action_mailer_tls plugin, then all you have to do is configure Action Mailer: <ruby> ActionMailer::Base.smtp_settings = { diff --git a/railties/guides/source/active_record_basics.textile b/railties/guides/source/active_record_basics.textile index afff892fd4..bf6e3c8181 100644 --- a/railties/guides/source/active_record_basics.textile +++ b/railties/guides/source/active_record_basics.textile @@ -69,7 +69,7 @@ h4. Schema Conventions ActiveRecord uses naming conventions for the columns in database tables, depending on the purpose of these columns. * *Foreign keys* - These fields should be named following the pattern table_id i.e. (item_id, order_id). These are the fields that ActiveRecord will look for when you create associations between your models. -* *Primary keys* - By default, ActiveRecord will use a integer column named "id" as the table's primary key. When using "Rails Migrations":http://guides.rails.info/migrations.html to create your tables, this column will be automatically created. +* *Primary keys* - By default, ActiveRecord 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 ActiveRecord instances: @@ -127,9 +127,9 @@ end h3. Validations -ActiveRecord gives the ability to validate the state of your models before they get recorded into the database. There are several methods that you can use to hook into the life-cycle of your models and validate that an attribute value is not empty or follow a specific format and so on. You can learn more about validations in the "Active Record Validations and Callbacks guide":http://guides.rails.info/activerecord_validations_callbacks.html#_overview_of_activerecord_validation. +ActiveRecord gives the ability to validate the state of your models before they get recorded into the database. There are several methods that you can use to hook into the life-cycle of your models and validate that an attribute value is not empty or follow a specific format and so on. You can learn more about validations in the "Active Record Validations and Callbacks guide":activerecord_validations_callbacks.html#validations-overview. h3. Callbacks -ActiveRecord callbacks allow you to attach code to certain events in the life-cycle of your models. This way you can add behavior to your models by transparently executing code when those events occur, like when you create a new record, update it, destroy it and so on. You can learn more about callbacks in the "Active Record Validations and Callbacks guide":http://guides.rails.info/activerecord_validations_callbacks.html#_callbacks. +ActiveRecord callbacks allow you to attach code to certain events in the life-cycle of your models. This way you can add behavior to your models by transparently executing code when those events occur, like when you create a new record, update it, destroy it and so on. You can learn more about callbacks in the "Active Record Validations and Callbacks guide":activerecord_validations_callbacks.html#callbacks-overview. diff --git a/railties/guides/source/active_record_querying.textile b/railties/guides/source/active_record_querying.textile index b112c4f5fb..302dad4f1a 100644 --- a/railties/guides/source/active_record_querying.textile +++ b/railties/guides/source/active_record_querying.textile @@ -6,7 +6,6 @@ This guide covers different ways to retrieve data from the database using Active * Specify the order, retrieved attributes, grouping, and other properties of the found records * Use eager loading to reduce the number of database queries needed for data retrieval * Use dynamic finders methods -* Create named scopes to add custom finding behavior to your models * Check for the existence of particular records * Perform various calculations on Active Record models @@ -23,7 +22,6 @@ TIP: All of the following models uses +id+ as the primary key, unless specified <ruby> class Client < ActiveRecord::Base has_one :address - has_one :mailing_address has_many :orders has_and_belongs_to_many :roles end @@ -36,11 +34,6 @@ end </ruby> <ruby> -class MailingAddress < Address -end -</ruby> - -<ruby> class Order < ActiveRecord::Base belongs_to :client, :counter_cache => true end @@ -245,7 +238,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 a argument from somewhere, or perhaps from the user's level status somewhere? The find then becomes something like: +Now what if that number could vary, say as an argument from somewhere, or perhaps from the user's level status somewhere? The find then becomes something like: <ruby> Client.first(:conditions => ["orders_count = ?", params[:orders]]) @@ -465,7 +458,7 @@ Be careful because this also means you're initializing a model object with only ActiveRecord::MissingAttributeError: missing attribute: <attribute> </shell> -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. +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: @@ -539,7 +532,7 @@ This will return single order objects for each day, but only for the last month. h4. Readonly Objects -To explicitly disallow modification/destroyal of the matching records returned by +Model.find+, you could specify the +:readonly+ option as +true+ to the find call. +To explicitly disallow modification/destruction of the matching records returned by +Model.find+, you could specify the +:readonly+ option as +true+ to the find call. Any attempt to alter or destroy the readonly records will not succeed, raising an +ActiveRecord::ReadOnlyRecord+ exception. To set this option, specify it like this: @@ -807,12 +800,12 @@ For every field (also known as an attribute) you define in your table, Active Re You can do +find_last_by_*+ methods too which will find the last record matching your argument. -You can specify an exclamation point (!) on the end of the dynamic finders to get them to raise an +ActiveRecord::RecordNotFound+ error if they do not return any records, like +Client.find_by_name!("Ryan")+ +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_name_and_locked("Ryan", true)+. -There's another set of dynamic finders that let you find or create/initialize objects if they aren't found. These work in a similar fashion to the other finders and can be used like +find_or_create_by_name(params[:name])+. Using this will firstly perform a find and then create if the find returns nil. The SQL looks like this for +Client.find_or_create_by_name("Ryan")+: +There's another set of dynamic finders that let you find or create/initialize objects if they aren't found. These work in a similar fashion to the other finders and can be used like +find_or_create_by_name(params[:name])+. Using this will firstly perform a find and then create if the find returns +nil+. The SQL looks like this for +Client.find_or_create_by_name("Ryan")+: <sql> SELECT * FROM clients WHERE (clients.name = 'Ryan') LIMIT 1 diff --git a/railties/guides/source/active_support_overview.textile b/railties/guides/source/active_support_overview.textile new file mode 100644 index 0000000000..aea77c8d4e --- /dev/null +++ b/railties/guides/source/active_support_overview.textile @@ -0,0 +1,818 @@ +h2. Active Support Overview + +Active Support is the Rails component responsible for providing Ruby language extensions, utilities, and other transversal stuff. It offers a richer bottom-line at the language level, targeted both at the development of Rails applications, and at the development of Rails itself. + +By referring to this guide you will learn: + +* The extensions to the Ruby core modules and classes provided by Rails. +* The rest of fundamental libraries available in Rails. + +endprologue. + +h3. Extensions to All Objects + +h4. +blank?+ and +present?+ + +The following values are considered to be blank in a Rails application: + +* +nil+ and +false+, + +* strings composed only of whitespace, i.e. matching +/\A\s*\z/+, + +* empty arrays and hashes, and + +* any other object that responds to +empty?+ and it is empty. + +WARNING: Note that numbers are not mentioned, in particular 0 and 0.0 are *not* blank. + +For example, this method from +ActionDispatch::Response+ uses +blank?+ to easily be robust to +nil+ and whitespace strings in one shot: + +<ruby> +def charset + charset = String(headers["Content-Type"] || headers["type"]).split(";")[1] + charset.blank? ? nil : charset.strip.split("=")[1] +end +</ruby> + +That's a typical use case for +blank?+. + +Here, the method Rails runs to instantiate observers upon initialization has nothing to do if there are none: + +<ruby> +def instantiate_observers + return if @observers.blank? + # ... +end +</ruby> + +The method +present?+ is equivalent to +!blank?+: + +<ruby> +assert @response.body.present? # same as !@response.body.blank? +</ruby> + +h4. +duplicable?+ + +A few fundamental objects in Ruby are singletons. For example, in the whole live of a program the integer 1 refers always to the same instance: + +<ruby> +1.object_id # => 3 +Math.cos(0).to_i.object_id # => 3 +</ruby> + +Hence, there's no way these objects can be duplicated through +dup+ or +clone+: + +<ruby> +true.dup # => TypeError: can't dup TrueClass +</ruby> + +Some numbers which are not singletons are not duplicable either: + +<ruby> +0.0.clone # => allocator undefined for Float +(2**1024).clone # => allocator undefined for Bignum +</ruby> + +Active Support provides +duplicable?+ to programmatically query an object about this property: + +<ruby> +"".duplicable? # => true +false.duplicable? # => false +</ruby> + +By definition all objects are +duplicable?+ except +nil+, +false+, +true+, symbols, numbers, and class objects. + +WARNING. Using +duplicable?+ is discouraged because it depends on a hard-coded list. Classes have means to disallow duplication like removing +dup+ and +clone+ or raising exceptions from them, only +rescue+ can tell. + +h4. +returning+ + +The method +returning+ yields its argument to a block and returns it. You tipically use it with a mutable object that gets modified in the block: + +<ruby> +def html_options_for_form(url_for_options, options, *parameters_for_url) + returning options.stringify_keys do |html_options| + html_options["enctype"] = "multipart/form-data" if html_options.delete("multipart") + html_options["action"] = url_for(url_for_options, *parameters_for_url) + end +end +</ruby> + +See also "+Object#tap+":#tap. + +h4. +tap+ + ++Object#tap+ exists in Ruby 1.8.7 and 1.9, and it is defined by Active Support for previous versions. This method yields its receiver to a block and returns it. + +For example, the following class method from +ActionDispatch::TestResponse+ creates, initializes, and returns a new test response using +tap+: + +<ruby> +def self.from_response(response) + new.tap do |resp| + resp.status = response.status + resp.headers = response.headers + resp.body = response.body + end +end +</ruby> + +See also "+Object#returning+":#returning. + +h4. +try+ + +Sometimes you want to call a method provided the receiver object is not +nil+, which is something you usually check first. + +For instance, note how this method of +ActiveRecord::ConnectionAdapters::AbstractAdapter+ checks if there's a +@logger+: + +<ruby> +def log_info(sql, name, ms) + if @logger && @logger.debug? + name = '%s (%.1fms)' % [name || 'SQL', ms] + @logger.debug(format_log_entry(name, sql.squeeze(' '))) + end +end +</ruby> + +You can shorten that using +Object#try+. This method is a synonim for +Object#send+ except that it returns +nil+ if sent to +nil+. The previous example could then be rewritten as: + +<ruby> +def log_info(sql, name, ms) + if @logger.try(:debug?) + name = '%s (%.1fms)' % [name || 'SQL', ms] + @logger.debug(format_log_entry(name, sql.squeeze(' '))) + end +end +</ruby> + +h4. +metaclass+ + +The method +metaclass+ returns the singleton class on any object: + +<ruby> +String.metaclass # => #<Class:String> +String.new.metaclass # => #<Class:#<String:0x17a1d1c>> +</ruby> + +h4. +class_eval(*args, &block)+ + +You can evaluate code in the context of any object's singleton class using +class_eval+: + +<ruby> +class Proc + def bind(object) + block, time = self, Time.now + object.class_eval do + method_name = "__bind_#{time.to_i}_#{time.usec}" + define_method(method_name, &block) + method = instance_method(method_name) + remove_method(method_name) + method + end.bind(object) + end +end +</ruby> + +h4. +acts_like?(duck)+ + +The method +acts_like+ provides a way to check whether some class acts like some other class based on a simple convention: a class that provides the same interface as +String+ defines + +<ruby> +def acts_like_string? +end +</ruby> + +which is only a marker, its body or return value are irrelevant. Then, client code can query for duck-type-safeness this way: + +<ruby> +some_klass.acts_like?(:string) +</ruby> + +Rails has classes that act like +Date+ or +Time+ and follow this contract. + +h4. +to_param+ + +All objects in Rails respond to the method +to_param+, which is meant to return something that represents them as values in a query string, or as a URL fragments. + +By default +to_param+ just calls +to_s+: + +<ruby> +7.to_param # => "7" +</ruby> + +The return value of +to_param+ should *not* be escaped: + +<ruby> +"Tom & Jerry".to_param # => "Tom & Jerry" +</ruby> + +Several classes in Rails overwrite this method. + +For example +nil+, +true+, and +false+ return themselves. +Array#to_param+ calls +to_param+ on the elements and joins the result with "/": + +<ruby> +[0, true, String].to_param # => "0/true/String" +</ruby> + +Notably, the Rails routing system calls +to_param+ on models to get a value for the +:id+ placeholder. +ActiveRecord::Base#to_param+ returns the +id+ of a model, but you can redefine that method in your models. For example, given + +<ruby> +class User + def to_param + "#{id}-#{name.parameterize}" + end +end +</ruby> + +we get: + +<ruby> +user_path(@user) # => "/users/357-john-smith" +</ruby> + +WARNING. Controllers need to be aware of any redifinition of +to_param+ because when a request like that comes in "357-john-smith" is the value of +params[:id]+. + +h4. +to_query+ + +Except for hashes, given an unescaped +key+ this method constructs the part of a query string that would map such key to what +to_param+ returns. For example, given + +<ruby> +class User + def to_param + "#{id}-#{name.parameterize}" + end +end +</ruby> + +we get: + +<ruby> +current_user.to_query('user') # => user=357-john-smith +</ruby> + +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" +</ruby> + +so its output is ready to be used in a query string. + +Arrays return the result of applying +to_query+ to each element with <tt>_key_[]</tt> as key, and join the result with "/": + +<ruby> +[3.4, -45.6].to_query('sample') +# => "sample%5B%5D=3.4&sample%5B%5D=-45.6" +</ruby> + +Hashes also respond to +to_query+ but with a different signature. If no argument is passed a call generates a sorted series of key/value assigments calling +to_query(key)+ on its values. Then it joins the result with "&": + +<ruby> +{:c => 3, :b => 2, :a => 1}.to_query # => "a=1&b=2&c=3" +</ruby> + +The method +Hash#to_query+ accepts an optional namespace for the keys: + +<ruby> +{:id => 89, :name => "John Smith"}.to_query('user') +# => "user%5Bid%5D=89&user%5Bname%5D=John+Smith" +</ruby> + +h4. +with_options+ + +The method +with_options+ provides a way to factor out common options in a series of method calls. + +Given a default options hash, +with_options+ yields a proxy object to a block. Within the block, methods called on the proxy are forwarded to the receiver with their options merged. For example, you get rid of the duplication in: + +<ruby> +class Account < ActiveRecord::Base + has_many :customers, :dependent => :destroy + has_many :products, :dependent => :destroy + has_many :invoices, :dependent => :destroy + has_many :expenses, :dependent => :destroy +end +</ruby> + +this way: + +<ruby> +class Account < ActiveRecord::Base + with_options :dependent => :destroy do |assoc| + assoc.has_many :customers + assoc.has_many :products + assoc.has_many :invoices + assoc.has_many :expenses + end +end +</ruby> + +That idiom may convey _grouping_ to the reader as well. For example, say you want to send a newsletter whose language depends on the user. Somewhere in the mailer you could group locale-dependent bits like this: + +<ruby> +I18n.with_options :locale => user.locale, :scope => "newsletter" do |i18n| + subject i18n.t :subject + body i18n.t :body, :user_name => user.name +end +</ruby> + +TIP: Since +with_options+ forwards calls to its receiver they can be nested. Each nesting level will merge inherited defaults in addition to their own. + +h4. Instance Variables + +Active Support provides several methods to ease access to instance variables. + +h5. +instance_variable_defined?+ + +The method +instance_variable_defined?+ exists in Ruby 1.8.6 and later, and it is defined for previous versions anyway: + +<ruby> +class C + def initialize + @a = 1 + end + + def m + @b = 2 + end +end + +c = C.new + +c.instance_variable_defined?("@a") # => true +c.instance_variable_defined?(:@a) # => true +c.instance_variable_defined?("a") # => NameError: `a' is not allowed as an instance variable name + +c.instance_variable_defined?("@b") # => false +c.m +c.instance_variable_defined?("@b") # => true +</ruby> + +h5. +instance_variable_names+ + +Ruby 1.8 and 1.9 have a method called +instance_variables+ that returns the names of the defined instance variables. But they behave differently, in 1.8 it returns strings whereas in 1.9 it returns symbols. Active Support defines +instance_variable_names+ as a portable way to obtain them as strings: + +<ruby> +class C + def initialize(x, y) + @x, @y = x, y + end +end + +C.new(0, 1).instance_variable_names # => ["@y", "@x"] +</ruby> + +WARNING: The order in which the names are returned is unespecified, and it indeed depends on the version of the interpreter. + +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: + +<ruby> +class C + def initialize(x, y) + @x, @y = x, y + end +end + +C.new(0, 1).instance_values # => {"x" => 0, "y" => 1} +</ruby> + +h5. +copy_instance_variables_from(object, exclude = [])+ + +Copies the instance variables of +object+ into +self+. + +Instance variable names in the +exclude+ array are ignored. If +object+ +responds to +protected_instance_variables+ the ones returned are +also ignored. For example, Rails controllers implement that method. + +In both arrays strings and symbols are understood, and they have to include +the at sign. + +<ruby> +class C + def initialize(x, y, z) + @x, @y, @z = x, y, z + end + + def protected_instance_variables + %w(@z) + end +end + +a = C.new(0, 1, 2) +b = C.new(3, 4, 5) + +a.copy_instance_variables_from(b, [:@y]) +# a is now: @x = 3, @y = 1, @z = 2 +</ruby> + +In the example +object+ and +self+ are of the same type, but they don't need to. + +h4. Silencing Warnings, Streams, and Exceptions + +The methods +silence_warnings+ and +enable_warnings+ change the value of +$VERBOSE+ accordingly for the duration of their block, and reset it afterwards: + +<ruby> +silence_warnings { Object.const_set "RAILS_DEFAULT_LOGGER", logger } +</ruby> + +You can silence any stream while a block runs with +silence_stream+: + +<ruby> +silence_stream(STDOUT) do + # STDOUT is silent here +end +</ruby> + +Silencing exceptions is also possible with +suppress+. This method receives an arbitrary number of exception classes. If an exception is raised during the execution of the block and is +kind_of?+ any of the arguments, +suppress+ captures it and returns silently. Otherwise the exception is reraised: + +<ruby> +# If the user is locked the increment is lost, no big deal. +suppress(ActiveRecord::StaleObjectError) do + current_user.increment! :visits +end +</ruby> + +h3. Extensions to +Module+ + +... + +h3. Extensions to +Class+ + +h4. Class Attribute Accessors + +The macros +cattr_reader+, +cattr_writer+, and +cattr_accessor+ are analogous to their +attr_*+ counterparts but for classes. They initialize a class variable to +nil+ unless it already exists, and generate the corresponding class methods to access it: + +<ruby> +class MysqlAdapter < AbstractAdapter + # Generates class methods to access @@emulate_booleans. + cattr_accessor :emulate_booleans + self.emulate_booleans = true +end +</ruby> + +Instance methods are created as well for convenience. For example given + +<ruby> +module ActionController + class Base + cattr_accessor :logger + end +end +</ruby> + +we can access +logger+ in actions. The generation of the writer instance method can be prevented setting +:instance_writer+ to +false+ (not any false value, but exactly +false+): + +<ruby> +module ActiveRecord + class Base + # No pluralize_table_names= instance writer is generated. + cattr_accessor :pluralize_table_names, :instance_writer => false + end +end +</ruby> + +h4. Class Inheritable Attributes + +Class variables are shared down the inheritance tree. Class instance variables are not shared, but they are not inherited either. The macros +class_inheritable_reader+, +class_inheritable_writer+, and +class_inheritable_accessor+ provide accesors for class-level data which is inherited but not shared with children: + +<ruby> +module ActionController + class Base + # FIXME: REVISE/SIMPLIFY THIS COMMENT. + # The value of allow_forgery_protection is inherited, + # but its value in a particular class does not affect + # the value in the rest of the controllers hierarchy. + class_inheritable_accessor :allow_forgery_protection + end +end +</ruby> + +They accomplish this with class instance variables and cloning on subclassing, there are no class variables involved. Cloning is performed with +dup+ as long as the value is duplicable. + +There are some variants specialised in arrays and hashes: + +<ruby> +class_inheritable_array +class_inheritable_hash +</ruby> + +Those writers take any inherited array or hash into account and extend them rather than overwrite them. + +As with vanilla class attribute accessors these macros create convenience instance methods for reading and writing. The generation of the writer instance method can be prevented setting +:instance_writer+ to +false+ (not any false value, but exactly +false+): + +<ruby> +module ActiveRecord + class Base + class_inheritable_accessor :default_scoping, :instance_writer => false + end +end +</ruby> + +Since values are copied when a subclass is defined, if the base class changes the attribute after that, the subclass does not see the new value. That's the point. + +There's a related macro called +superclass_delegating_accessor+, however, that does not copy the value when the base class is subclassed. Instead, it delegates reading to the superclass as long as the attribute is not set via its own writer. For example, +ActionMailer::Base+ defines +delivery_method+ this way: + +<ruby> +module ActionMailer + class Base + superclass_delegating_accessor :delivery_method + self.delivery_method = :smtp + end +end +</ruby> + +If for whatever reason an application loads the definition of a mailer class and after that sets +ActionMailer::Base.delivery_method+, the mailer class will still see the new value. In addition, the mailer class is able to change the +delivery_method+ without affecting the value in the parent using its own inherited class attribute writer. + +h4. Subclasses + +The +subclasses+ method returns the names of all subclasses of a given class as an array of strings. That comprises not only direct subclasses, but all descendants down the hierarchy: + +<ruby> +class C; end +C.subclasses # => [] + +Integer.subclasses # => ["Bignum", "Fixnum"] + +module M + class A; end + class B1 < A; end + class B2 < A; end +end + +module N + class C < M::B1; end +end + +M::A.subclasses # => ["N::C", "M::B2", "M::B1"] +</ruby> + +The order in which these class names are returned is unspecified. + +See also +Object#subclasses_of+ in "Extensions to All Objects FIX THIS LINK":FIXME. + +h4. Class Removal + +Roughly speaking, the +remove_class+ method removes the class objects passed as arguments: + +<ruby> +Class.remove_class(Hash, Dir) # => [Hash, Dir] +Hash # => NameError: uninitialized constant Hash +Dir # => NameError: uninitialized constant Dir +</ruby> + +More specifically, +remove_class+ attempts to remove constants with the same name as the passed class objects from their parent modules. So technically this method does not guarantee the class objects themselves are not still valid and alive somewhere after the method call: + +<ruby> +module M + class A; end + class B < A; end +end + +A2 = M::A + +M::A.object_id # => 13053950 +Class.remove_class(M::A) + +M::B.superclass.object_id # => 13053950 (same object as before) +A2.name # => "M::A" (name is hard-coded in object) +</ruby> + +WARNING: Removing fundamental classes like +String+ can result in really funky behaviour. + +The method +remove_subclasses+ provides a shortcut for removing all descendants of a given class, where "removing" has the meaning explained above: + +<ruby> +class A; end +class B1 < A; end +class B2 < A; end +class C < A; end + +A.subclasses # => ["C", "B2", "B1"] +A.remove_subclasses +A.subclasses # => [] +C # => NameError: uninitialized constant C +</ruby> + +See also +Object#remove_subclasses_of+ in "Extensions to All Objects FIX THIS LINK":FIXME. + +h3. Extensions to +NilClass+ + +... + +h3. Extensions to +TrueClass+ + +... + +h3. Extensions to +FalseClass+ + +... + +h3. Extensions to +Symbol+ + +... + +h3. Extensions to +String+ + +... + +h3. Extensions to +Numeric+ + +... + +h3. Extensions to +Integer+ + +... + +h3. Extensions to +Float+ + +... + +h3. Extensions to +BigDecimal+ + +... + +h3. Extensions to +Enumerable+ + +... + +h3. Extensions to +Array+ + +h4. Accessing + +Active Support augments the API of arrays to ease certain ways of accessing them. For example, +to+ returns the subarray of elements up to the one at the passed index: + +<ruby> +%w(a b c d).to(2) # => %w(a b c) +[].to(7) # => [] +</ruby> + +Similarly, +from+ returns the tail from the element at the passed index on: + +<ruby> +%w(a b c d).from(2) # => %w(c d) +%w(a b c d).from(10) # => nil +[].from(0) # => nil +</ruby> + +The methods +second+, +third+, +fourth+, and +fifth+ return the corresponding element (+first+ is builtin). Thanks to social wisdom and positive constructiveness all around, +forty_two+ is also available. + +You can pick a random element with +rand+: + +<ruby> +shape_type = [Circle, Square, Triangle].rand +</ruby> + +h4. Grouping + +h5. +in_groups_of(number, fill_with = nil)+ + +The method +in_groups_of+ splits an array into consecutive groups of a certain size. It returns an array with the groups: + +<ruby> +[1, 2, 3].in_groups_of(2) # => [[1, 2], [3, nil]] +</ruby> + +or yields them in turn if a block is passed: + +<ruby> +<% sample.in_groups_of(3) do |a, b, c| %> + <tr> + <td><%=h a %></td> + <td><%=h b %></td> + <td><%=h c %></td> + </tr> +<% end %> +</ruby> + +The first example shows +in_groups_of+ fills the last group with as many +nil+ elements as needed to have the requested size. You can change this padding value using the second optional argument: + +<ruby> +[1, 2, 3].in_groups_of(2, 0) # => [[1, 2], [3, 0]] +</ruby> + +And you can tell the method not to fill the last group passing +false+: + +<ruby> +[1, 2, 3].in_groups_of(2, false) # => [[1, 2], [3]] +</ruby> + +As a consequence +false+ can't be a used as a padding value. + +h5. +in_groups(number, fill_with = nil)+ + +The method +in_groups+ splits an array into a certain number of groups. The method returns and array with the groups: + +<ruby> +%w(1 2 3 4 5 6 7).in_groups(3) +# => [["1", "2", "3"], ["4", "5", nil], ["6", "7", nil]] +</ruby> + +or yields them in turn if a block is passed: + +<ruby> +%w(1 2 3 4 5 6 7).in_groups(3) {|group| p group} +["1", "2", "3"] +["4", "5", nil] +["6", "7", nil] +</ruby> + +The examples above show that +in_groups+ fills some groups with a trailing +nil+ element as needed. A group can get at most one of these extra elements, the rightmost one if any. And the groups that have them are always the last ones. + +You can change this padding value using the second optional argument: + +<ruby> +%w(1 2 3 4 5 6 7).in_groups(3, "0") +# => [["1", "2", "3"], ["4", "5", "0"], ["6", "7", "0"]] +</ruby> + +And you can tell the method not to fill the smaller groups passing +false+: + +<ruby> +%w(1 2 3 4 5 6 7).in_groups(3, false) +# => [["1", "2", "3"], ["4", "5"], ["6", "7"]] +</ruby> + +As a consequence +false+ can't be a used as a padding value. + +h5. +split(value = nil)+ + +The method +split+ divides an array by a separator and returns the resulting chunks. + +If a block is passed the separators are those elements of the array for which the block returns true: + +<ruby> +(-5..5).to_a.split { |i| i.multiple_of?(4) } +# => [[-5], [-3, -2, -1], [1, 2, 3], [5]] +</ruby> + +Otherwise, the value received as argument, which defaults to +nil+, is the separator: + +<ruby> +[0, 1, -5, 1, 1, "foo", "bar"].split(1) +# => [[0], [-5], [], ["foo", "bar"]] +</ruby> + +NOTE: Observe in the previous example that consecutive separators result in empty arrays. + +h3. Extensions to +Hash+ + +... + +h3. Extensions to +Range+ + +... + +h3. Extensions to +Proc+ + +... + +h3. Extensions to +Date+ + +... + +h3. Extensions to +DateTime+ + +... + +h3. Extensions to +Time+ + +... + +h3. Extensions to +Process+ + +... + +h3. Extensions to +Pathname+ + +... + +h3. Extensions to +File+ + +... + +h3. Extensions to +Exception+ + +... + +h3. Extensions to +NameError+ + +... + +h3. Extensions to +LoadError+ + +... + +h3. Extensions to +CGI+ + +... + +h3. Extensions to +Benchmark+ + +... + +h3. Changelog + +"Lighthouse ticket":https://rails.lighthouseapp.com/projects/16213/tickets/67 + +* April 18, 2009: Initial version by "Xavier Noria":credits.html#fxn diff --git a/railties/guides/source/activerecord_validations_callbacks.textile b/railties/guides/source/activerecord_validations_callbacks.textile index 5ae4884297..03d521ea1f 100644 --- a/railties/guides/source/activerecord_validations_callbacks.textile +++ b/railties/guides/source/activerecord_validations_callbacks.textile @@ -338,7 +338,7 @@ end Besides +:only_integer+, the +validates_numericality_of+ helper also accepts the following options to add constraints to acceptable values: * +:greater_than+ - Specifies the value must be greater than the supplied value. The default error message for this option is "_must be greater than {{count}}_". -* +:greater_than_or_equal_to+ - Specifies the value must be greater than or equal to the supplied value. The default error message for this option is "_must be greater than or equal to {{count}}". +* +:greater_than_or_equal_to+ - Specifies the value must be greater than or equal to the supplied value. The default error message for this option is "_must be greater than or equal to {{count}}_". * +:equal_to+ - Specifies the value must be equal to the supplied value. The default error message for this option is "_must be equal to {{count}}_". * +:less_than+ - Specifies the value must be less than the supplied value. The default error message for this option is "_must be less than {{count}}_". * +:less_than_or_equal_to+ - Specifies the value must be less than or equal the supplied value. The default error message for this option is "_must be less or equal to {{count}}_". @@ -530,11 +530,11 @@ class Invoice < ActiveRecord::Base end </ruby> -You can even create your own validation helpers and reuse them in several different models. Here is an example where we create a custom validation helper to validate the format of fields that represent email addresses: +You can even create your own validation helpers and reuse them in several different models. For example, an application that manages surveys may find useful to express that a certain field corresponds to a set of choices: <ruby> ActiveRecord::Base.class_eval do - def self.validates_as_radio(attr_name, n, options={}) + def self.validates_as_choice(attr_name, n, options={}) validates_inclusion_of attr_name, {:in => 1..n}.merge(options) end end @@ -544,7 +544,7 @@ Simply reopen +ActiveRecord::Base+ and define a class method like that. You'd ty <ruby> class Movie < ActiveRecord::Base - validates_as_radio :rating, 5 + validates_as_choice :rating, 5 end </ruby> diff --git a/railties/guides/source/ajax_on_rails.textile b/railties/guides/source/ajax_on_rails.textile new file mode 100644 index 0000000000..74e8dec5cf --- /dev/null +++ b/railties/guides/source/ajax_on_rails.textile @@ -0,0 +1,94 @@ +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, RJS, Prototype and script.aculo.us +* Testing Javascript functionality +* Becoming an Ajax Master on Rails: Plugins, Best Practices, Tips and Tricks + +endprologue. + +h3. Hello AJAX - a Quick Intro + +If you are a 'show me the code' type of person, you might want to skip this part and jump to the RJS section right away. However, I would really recommend to read it - you'll need the basics of DOM, http requests and other topics discussed here to really understand Ajax on Rails. + +h4. Asynchronous Javascript + XML + +Basic terminology, new style of creating web apps + +h4. The DOM + +basics of the DOM, how is it built, properties, features, why is it central to AJAX + +h4. Standard HTML communication vs AJAX + +How do 'standard' and AJAX requests differ, why does this matter for understanding AJAX on Rails (tie in for *_remote helpers, the next section) + + + + + + +h3. Built-in Rails Helpers + +Mostly a reference to standard JS helpers like link_to_remote, remote_form_for etc + some explanation + + + +h3. Responding to AJAX the Rails way: RJS + +In the last section we sent some AJAX requests to the server; now we need to respond, and the standard Rails way to this is using RJS; RJS intro, function reference + + + +h3. I Want my Yellow Thingy: Prototype and Script.aculo.us + +Walk through prototype and script.aculo.us, most important functionality, method reference etc. + + + +h3. Testing Javascript + +Javascript testing reminds me the definition of the world 'classic' by Mark Twain: "A classic is something that everybody wants to have read and nobody wants to read." It's similar with Javascript testing: everyone would like to have it, yet it's not done by too much developers as it is tedious, complicated, there is a proliferation of tools and no consensus/accepted best practices, but we will nevertheless take a stab at it: + +* (Fire)Watir +* Selenium +* Celerity/Culerity +* Cucumber+Webrat +* Mention stuff like screw.unit/jsSpec + +Note to self: check out the RailsConf JS testing video + +h3. Useful Plugins + +This was in the ticket description, but at the moment I don't really have clue what to add here, so please tell me + + + +h3. Tips and Tricks + +* Unobtrusive Javascript (Prototype events, maybe the jQuery way (esp. jQeury.live())) + +* Minimize communication with the server - there does not have to be a communication at all! +** If you absolutely don't have to, don't use Rails observers +** Cache stuff on the client side, e.g. with auto-complete + +* Using AJAX to load stuff asynchronously +** To avoid page blocking +** Tricking page caching +*** inserting user-specific info into a cached page +*** anti-CSFR bit + +* Jumping to the top? Try event.stopPropagation + +* Performance +** pack your javascript (minify, asset packager) +** require your JS at the end of the file +** other perf tricks and optimization + +* Don't overuse AJAX +** Usability first, cool effects second +** situations where AJAX is discouraged + +* Last but not least: Javascript is your friend :) diff --git a/railties/guides/source/association_basics.textile b/railties/guides/source/association_basics.textile index 03e22bd6fe..ca10014ee0 100644 --- a/railties/guides/source/association_basics.textile +++ b/railties/guides/source/association_basics.textile @@ -30,7 +30,7 @@ Now, suppose we wanted to add a new order for an existing customer. We'd need to Or consider deleting a customer, and ensuring that all of its orders get deleted as well: <ruby> -@orders = Order.find_by_customer_id(@customer.id) +@orders = Order.find_all_by_customer_id(@customer.id) @orders.each do |order| order.destroy end @@ -600,6 +600,7 @@ The +belongs_to+ association supports these options: * +:polymorphic+ * +:readonly+ * +:select+ +* +:touch+ * +:validate+ h6. +:autosave+ @@ -736,6 +737,28 @@ The +:select+ option lets you override the SQL +SELECT+ clause that is used to r TIP: If you set the +:select+ option on a +belongs_to+ association, you should also set the +foreign_key+ option to guarantee the correct results. +h6. +:touch+ + +If you set the +:touch+ option to +:true+, then the +updated_at+ or +updated_on+ timestamp on the associated object will be set to the current time whenever this object is saved or destroyed: + +<ruby> +class Order < ActiveRecord::Base + belongs_to :customer, :touch => true +end + +class Customer < ActiveRecord::Base + has_many :orders +end +</ruby> + +In this case, saving or destroying an order will update the timestamp on the associated customer. You can also specify a particular timestamp attribute to update: + +<ruby> +class Order < ActiveRecord::Base + belongs_to :customer, :touch => :orders_updated_at +end +</ruby> + h6. +: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. @@ -996,7 +1019,7 @@ When you declare a +has_many+ association, the declaring class automatically gai * <tt><em>collection</em>.empty?</tt> * <tt><em>collection</em>.size</tt> * <tt><em>collection</em>.find(...)</tt> -* <tt><em>collection</em>.exist?(...)</tt> +* <tt><em>collection</em>.exists?(...)</tt> * <tt><em>collection</em>.build(attributes = {}, ...)</tt> * <tt><em>collection</em>.create(attributes = {})</tt> @@ -1021,7 +1044,7 @@ orders.clear orders.empty? orders.size orders.find(...) -orders.exist?(...) +orders.exists?(...) orders.build(attributes = {}, ...) orders.create(attributes = {}) </ruby> @@ -1099,9 +1122,9 @@ The <tt><em>collection</em>.find</tt> method finds objects within the collection @open_orders = @customer.orders.find(:all, :conditions => "open = 1") </ruby> -h6. <tt><em>collection</em>.exist?(...)</tt> +h6. <tt><em>collection</em>.exists?(...)</tt> -The <tt><em>collection</em>.exist?</tt> method checks whether an object meeting the supplied conditions exists in the collection. It uses the same syntax and options as +ActiveRecord::Base.exists?+. +The <tt><em>collection</em>.exists?</tt> method checks whether an object meeting the supplied conditions exists in the collection. It uses the same syntax and options as +ActiveRecord::Base.exists?+. h6. <tt><em>collection</em>.build(attributes = {}, ...)</tt> @@ -1196,6 +1219,17 @@ 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: + +<ruby> +class Customer < ActiveRecord::Base + has_many :latest_orders, :class_name => "Order", + :conditions => 'orders.created_at > #{10.hours.ago.to_s(:db).inspect}' +end +</ruby> + +Be sure to use single quotes. + h6. +: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. @@ -1361,7 +1395,7 @@ When you declare a +has_and_belongs_to_many+ association, the declaring class au * <tt><em>collection</em>.empty?</tt> * <tt><em>collection</em>.size</tt> * <tt><em>collection</em>.find(...)</tt> -* <tt><em>collection</em>.exist?(...)</tt> +* <tt><em>collection</em>.exists?(...)</tt> * <tt><em>collection</em>.build(attributes = {})</tt> * <tt><em>collection</em>.create(attributes = {})</tt> @@ -1386,7 +1420,7 @@ assemblies.clear assemblies.empty? assemblies.size assemblies.find(...) -assemblies.exist?(...) +assemblies.exists?(...) assemblies.build(attributes = {}, ...) assemblies.create(attributes = {}) </ruby> @@ -1471,9 +1505,9 @@ The <tt><em>collection</em>.find</tt> method finds objects within the collection :conditions => ["created_at > ?", 2.days.ago]) </ruby> -h6. <tt><em>collection</em>.exist?(...)</tt> +h6. <tt><em>collection</em>.exists?(...)</tt> -The <tt><em>collection</em>.exist?</tt> method checks whether an object meeting the supplied conditions exists in the collection. It uses the same syntax and options as +ActiveRecord::Base.exists?+. +The <tt><em>collection</em>.exists?</tt> method checks whether an object meeting the supplied conditions exists in the collection. It uses the same syntax and options as +ActiveRecord::Base.exists?+. h6. <tt><em>collection</em>.build(attributes = {})</tt> @@ -1775,6 +1809,7 @@ h3. Changelog "Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/11 +* April 19, 2009: Added +:touch+ option to +belongs_to+ associations by "Mike Gunderloy":credits.html#mgunderloy * February 1, 2009: Added +:autosave+ option "Mike Gunderloy":credits.html#mgunderloy * September 28, 2008: Corrected +has_many :through+ diagram, added polymorphic diagram, some reorganization by "Mike Gunderloy":credits.html#mgunderloy . First release version. * September 22, 2008: Added diagrams, misc. cleanup by "Mike Gunderloy":credits.html#mgunderloy (not yet approved for publication) diff --git a/railties/guides/source/caching_with_rails.textile b/railties/guides/source/caching_with_rails.textile index f1ad7b820d..2865bc504a 100644 --- a/railties/guides/source/caching_with_rails.textile +++ b/railties/guides/source/caching_with_rails.textile @@ -1,8 +1,6 @@ h2. Caching with Rails: An overview -Everyone caches. This guide will teach you what you need to know about -avoiding that expensive round-trip to your database and returning what you -need to return to those hungry web clients in the shortest time possible. +This guide will teach you what you need to know about avoiding that expensive round-trip to your database and returning what you need to return to the web clients in the shortest time possible. After reading this guide, you should be able to use and configure: @@ -15,14 +13,9 @@ endprologue. h3. Basic Caching -This is an introduction to the three types of caching techniques that Rails -provides by default without the use of any third party plugins. +This is an introduction to the three types of caching techniques that Rails provides by default without the use of any third party plugins. -To start playing with testing you'll want to ensure that -+config.action_controller.perform_caching+ is set -to +true+ if you're running in development mode. This flag is normally set in the -corresponding config/environments/*.rb and caching is disabled by default - for development and test, and enabled for production. +To start playing with testing you'll want to ensure that +config.action_controller.perform_caching+ is set to +true+ if you're running in development mode. This flag is normally set in the corresponding +config/environments/*.rb+ and caching is disabled by default for development and test, and enabled for production. <ruby> config.action_controller.perform_caching = true @@ -30,16 +23,9 @@ config.action_controller.perform_caching = true h4. Page Caching -Page caching is a Rails mechanism which allows the request for a generated -page to be fulfilled by the webserver (i.e. apache or nginx), without ever having to go through the -Rails stack at all. Obviously, this is super-fast. Unfortunately, it can't be -applied to every situation (such as pages that need authentication) and since -the webserver is literally just serving a file from the filesystem, cache -expiration is an issue that needs to be dealt with. +Page caching is a Rails mechanism which allows the request for a generated page to be fulfilled by the webserver (i.e. apache or nginx), without ever having to go through the Rails stack at all. Obviously, this is super-fast. Unfortunately, it can't be applied to every situation (such as pages that need authentication) and since the webserver is literally just serving a file from the filesystem, cache expiration is an issue that needs to be dealt with. -So, how do you enable this super-fast cache behavior? Simple, let's say you -have a controller called +ProductsController+ and an +index+ action that lists all -the products +So, how do you enable this super-fast cache behavior? Simple, let's say you have a controller called +ProductsController+ and an +index+ action that lists all the products <ruby> class ProductsController < ActionController @@ -53,25 +39,13 @@ class ProductsController < ActionController end </ruby> -The first time anyone requests +/products+, Rails will generate a file -called +products.html+ and the webserver will then look for that file before it -passes the next request for +/products+ to your Rails application. +The first time anyone requests +/products+, Rails will generate a file called +products.html+ and the webserver will then look for that file before it passes the next request for +/products+ to your Rails application. -By default, the page cache directory is set to +Rails.public_path+ (which is -usually set to the +public+ folder) and this can be configured by -changing the configuration setting +config.action_controller.page_cache_directory+. -Changing the default from +public+ helps avoid naming conflicts, since you may -want to put other static html in +public+, but changing this will require web -server reconfiguration to let the web server know where to serve the cached -files from. +By default, the page cache directory is set to +Rails.public_path+ (which is usually set to the +public+ folder) and this can be configured by changing the configuration setting +config.action_controller.page_cache_directory+. Changing the default from +public+ helps avoid naming conflicts, since you may want to put other static html in +public+, but changing this will require web server reconfiguration to let the web server know where to serve the cached files from. -The Page Caching mechanism will automatically add a +.html+ extension to -requests for pages that do not have an extension to make it easy for the -webserver to find those pages and this can be configured by changing the -configuration setting +config.action_controller.page_cache_extension+. +The Page Caching mechanism will automatically add a +.html+ extension to requests for pages that do not have an extension to make it easy for the webserver to find those pages and this can be configured by changing the configuration setting +config.action_controller.page_cache_extension+. -In order to expire this page when a new product is added we could extend our -example controller like this: +In order to expire this page when a new product is added we could extend our example controller like this: <ruby> class ProductsController < ActionController @@ -83,26 +57,19 @@ class ProductsController < ActionController end def create - expire_page :action => :index + expire_page :action => :list end end </ruby> -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. +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! 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. +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. @@ -125,37 +92,19 @@ class ProductsController < ActionController end </ruby> -You can also use +:if+ (or +:unless+) to pass a Proc that specifies when the -action should be cached. Also, you can use +:layout => false+ to cache without -layout so that dynamic information in the layout such as logged in user info -or the number of items in the cart can be left uncached. This feature is -available as of Rails 2.2. +You can also use +:if+ (or +:unless+) to pass a Proc that specifies when the action should be cached. Also, you can use +:layout => false+ to cache without layout so that dynamic information in the layout such as logged in user info or the number of items in the cart can be left uncached. This feature is available as of Rails 2.2. -You can modify the default action cache path by passing a +:cache_path+ option. -This will be passed directly to +ActionCachePath.path_for+. This is handy for -actions with multiple possible routes that should be cached differently. If -a block is given, it is called with the current controller instance. +You can modify the default action cache path by passing a +:cache_path+ option. This will be passed directly to +ActionCachePath.path_for+. This is handy for actions with multiple possible routes that should be cached differently. If a block is given, it is called with the current controller instance. -Finally, if you are using memcached, you can also pass +:expires_in+. In fact, -all parameters not used by +caches_action+ are sent to the underlying cache -store. +Finally, if you are using memcached, you can also pass +:expires_in+. In fact, all parameters not used by +caches_action+ are sent to the underlying cache store. h4. Fragment Caching -Life would be perfect if we could get away with caching the entire contents of -a page or action and serving it out to the world. Unfortunately, dynamic web -applications usually build pages with a variety of components not all of which -have the same caching characteristics. In order to address such a dynamically -created page where different parts of the page need to be cached and expired -differently Rails provides a mechanism called Fragment Caching. +Life would be perfect if we could get away with caching the entire contents of a page or action and serving it out to the world. Unfortunately, dynamic web applications usually build pages with a variety of components not all of which have the same caching characteristics. In order to address such a dynamically created page where different parts of the page need to be cached and expired differently Rails provides a mechanism called Fragment Caching. -Fragment Caching allows a fragment of view logic to be wrapped in a cache -block and served out of the cache store when the next request comes in. +Fragment Caching allows a fragment of view logic to be wrapped in a cache block and served out of the cache store when the next request comes in. -As an example, if you wanted to show all the orders placed on your website -in real time and didn't want to cache that part of the page, but did want -to cache the part of the page which lists all products available, you -could use this piece of code: +As an example, if you wanted to show all the orders placed on your website in real time and didn't want to cache that part of the page, but did want to cache the part of the page which lists all products available, you could use this piece of code: <ruby> <% Order.find_recent.each do |o| %> @@ -170,9 +119,7 @@ could use this piece of code: <% end %> </ruby> -The cache block in our example will bind to the action that called it and is -written out to the same place as the Action Cache, which means that if you -want to cache multiple fragments per action, you should provide an +action_suffix+ to the cache call: +The cache block in our example will bind to the action that called it and is written out to the same place as the Action Cache, which means that if you want to cache multiple fragments per action, you should provide an +action_suffix+ to the cache call: <ruby> <% cache(:action => 'recent', :action_suffix => 'all_products') do %> @@ -185,9 +132,7 @@ and you can expire it using the +expire_fragment+ method, like so: expire_fragment(:controller => 'products', :action => 'recent', :action_suffix => 'all_products') </ruby> -If you don't want the cache block to bind to the action that called it, You can -also use globally keyed fragments by calling the +cache+ method with a key, like -so: +If you don't want the cache block to bind to the action that called it, You can also use globally keyed fragments by calling the +cache+ method with a key, like so: <ruby> <% cache('all_available_products') do %> @@ -195,8 +140,7 @@ so: <% end %> </ruby> -This fragment is then available to all actions in the +ProductsController+ using -the key and can be expired the same way: +This fragment is then available to all actions in the +ProductsController+ using the key and can be expired the same way: <ruby> expire_fragment('all_available_products') @@ -204,15 +148,9 @@ expire_fragment('all_available_products') h4. Sweepers -Cache sweeping is a mechanism which allows you to get around having a ton of -+expire_{page,action,fragment}+ calls in your code. It does this by moving all the work -required to expire cached content into a +ActionController::Caching::Sweeper+ -class. This class is an Observer and looks for changes to an object via callbacks, -and when a change occurs it expires the caches associated with that object in -an around or after filter. +Cache sweeping is a mechanism which allows you to get around having a ton of +expire_{page,action,fragment}+ calls in your code. It does this by moving all the work required to expire cached content into a +ActionController::Caching::Sweeper+ class. This class is an Observer and looks for changes to an object via callbacks, and when a change occurs it expires the caches associated with that object in an around or after filter. -Continuing with our Product controller example, we could rewrite it with a -sweeper like this: +Continuing with our Product controller example, we could rewrite it with a sweeper like this: <ruby> class ProductSweeper < ActionController::Caching::Sweeper @@ -244,18 +182,13 @@ class ProductSweeper < ActionController::Caching::Sweeper end </ruby> -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 a expire method -which specifies the page we want to expire: +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) </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: +Then we add it to our controller to tell it to call the sweeper when certain actions are called. So, if we wanted to expire the cached content for the list and edit actions when the create action was called, we could do the following: <ruby> class ProductsController < ActionController @@ -273,10 +206,7 @@ end h4. SQL Caching -Query caching is a Rails feature that caches the result set returned by each -query so that if Rails encounters the same query again for that request, it -will used the cached result set as opposed to running the query against the -database again. +Query caching is a Rails feature that caches the result set returned by each query so that if Rails encounters the same query again for that request, it will use the cached result set as opposed to running the query against the database again. For example: @@ -296,131 +226,85 @@ class ProductsController < ActionController end </ruby> -The second time the same query is run against the database, it's not actually -going to hit the database. The first time the result is returned from the query -it is stored in the query cache (in memory) and the second time it's pulled from memory. +The second time the same query is run against the database, it's not actually going to hit the database. The first time the result is returned from the query it is stored in the query cache (in memory) and the second time it's pulled from memory. -However, it's important to note that query caches are created at the start of an action and destroyed at the end of -that action and thus persist only for the duration of the action. If you'd like to store query results in a more -persistent fashion, you can in Rails by using low level caching. +However, it's important to note that query caches are created at the start of an action and destroyed at the end of that action and thus persist only for the duration of the action. If you'd like to store query results in a more persistent fashion, you can in Rails by using low level caching. -h4. Cache stores +h3. Cache Stores -Rails (as of 2.1) provides different stores for the cached data created by action and -fragment caches. Page caches are always stored on disk. +Rails provides different stores for the cached data created by action and fragment caches. Page caches are always stored on disk. -Rails 2.1 and above provide +ActiveSupport::Cache::Store+ which can be used to -cache strings. Some cache store implementations, like MemoryStore, are able to -cache arbitrary Ruby objects, but don't count on every cache store to be able -to do that. +Rails 2.1 and above provide +ActiveSupport::Cache::Store+ which can be used to cache strings. Some cache store implementations, like +MemoryStore+, are able to cache arbitrary Ruby objects, but don't count on every cache store to be able to do that. The default cache stores provided with Rails include: -1) ActiveSupport::Cache::MemoryStore: A cache store implementation which stores -everything into memory in the same process. If you're running multiple Ruby on -Rails server processes (which is the case if you're using mongrel_cluster or -Phusion Passenger), then this means that your Rails server process instances -won't be able to share cache data with each other. If your application never -performs manual cache item expiry (e.g. when you‘re using generational cache -keys), then using +MemoryStore+ is ok. Otherwise, consider carefully whether you -should be using this cache store. +1) +ActiveSupport::Cache::MemoryStore+: A cache store implementation which stores everything into memory in the same process. If you're running multiple Ruby on Rails server processes (which is the case if you're using mongrel_cluster or Phusion Passenger), then this means that your Rails server process instances won't be able to share cache data with each other. If your application never performs manual cache item expiry (e.g. when you‘re using generational cache keys), then using +MemoryStore+ is ok. Otherwise, consider carefully whether you should be using this cache store. -+MemoryStore+ is not only able to store strings, but also arbitrary Ruby objects. ++MemoryStore+ is not only able to store strings, but also arbitrary Ruby objects. -+MemoryStore+ is not thread-safe. Use +SynchronizedMemoryStore+ instead if you -need thread-safety. ++MemoryStore+ is not thread-safe. Use +SynchronizedMemoryStore+ instead if you need thread-safety. - <ruby> ActionController::Base.cache_store = :memory_store </ruby> -2) ActiveSupport::Cache::FileStore: Cached data is stored on the disk, this is -the default store and the default path for this store is: /tmp/cache. Works -well for all types of environments and allows all processes running from the -same application directory to access the cached content. If /tmp/cache does not -exist, the default store becomes MemoryStore. - +2) +ActiveSupport::Cache::FileStore+: Cached data is stored on the disk, this is the default store and the default path for this store is +tmp/cache+. Works well for all types of environments and allows all processes running from the same application directory to access the cached content. If +tmp/cache+ does not exist, the default store becomes +MemoryStore+. <ruby> ActionController::Base.cache_store = :file_store, "/path/to/cache/directory" </ruby> -3) ActiveSupport::Cache::DRbStore: Cached data is stored in a separate shared -DRb process that all servers communicate with. This works for all environments -and only keeps one cache around for all processes, but requires that you run -and manage a separate DRb process. - +3) +ActiveSupport::Cache::DRbStore+: Cached data is stored in a separate shared DRb process that all servers communicate with. This works for all environments and only keeps one cache around for all processes, but requires that you run and manage a separate DRb process. <ruby> ActionController::Base.cache_store = :drb_store, "druby://localhost:9192" </ruby> -4) MemCached store: Works like DRbStore, but uses Danga's MemCache instead. -Rails uses the bundled memcached-client gem by default. This is currently the -most popular cache store for production websites. +4) +ActiveSupport::Cache::MemCacheStore+: Works like +DRbStore+, but uses Danga's +memcached+ instead. Rails uses the bundled +memcached-client+ gem by default. This is currently the most popular cache store for production websites. Special features: - * Clustering and load balancing. One can specify multiple memcached servers, - and MemCacheStore will load balance between all available servers. If a - server goes down, then MemCacheStore will ignore it until it goes back - online. - * Time-based expiry support. See +write+ and the +:expires_in+ option. - * Per-request in memory cache for all communication with the MemCache server(s). -It also accepts a hash of additional options: +* Clustering and load balancing. One can specify multiple memcached servers, and +MemCacheStore+ will load balance between all available servers. If a server goes down, then +MemCacheStore+ will ignore it until it goes back online. +* Time-based expiry support. See +write+ and the +:expires_in+ option. +* Per-request in memory cache for all communication with the +memcached+ server(s). - * +:namespace+- specifies a string that will automatically be prepended to keys when accessing the memcached store. - * +:readonly+- a boolean value that when set to true will make the store read-only, with an error raised on any attempt to write. - * +:multithread+ - a boolean value that adds thread safety to read/write operations - it is unlikely you'll need to use this option as the Rails threadsafe! method offers the same functionality. +It also accepts a hash of additional options: -The read and write methods of the MemCacheStore accept an options hash too. -When reading you can specify +:raw => true+ to prevent the object being marshaled -(by default this is false which means the raw value in the cache is passed to -+Marshal.load+ before being returned to you.) +* +:namespace+: specifies a string that will automatically be prepended to keys when accessing the memcached store. +* +:readonly+: a boolean value that when set to true will make the store read-only, with an error raised on any attempt to write. +* +:multithread+: a boolean value that adds thread safety to read/write operations - it is unlikely you'll need to use this option as the Rails threadsafe! method offers the same functionality. -When writing to the cache it is also possible to specify +:raw => true+ means -the value is not passed to +Marshal.dump+ before being stored in the cache (by -default this is false). +The read and write methods of the +MemCacheStore+ accept an options hash too. When reading you can specify +:raw => true+ to prevent the object being marshaled (by default this is false which means the raw value in the cache is passed to +Marshal.load+ before being returned to you.) -The write method also accepts an +:unless_exist+ flag which determines whether -the memcached add (when true) or set (when false) method is used to store the -item in the cache and an +:expires_in+ option that specifies the time-to-live -for the cached item in seconds. +When writing to the cache it is also possible to specify +:raw => true+ means the value is not passed to +Marshal.dump+ before being stored in the cache (by default this is false). +The write method also accepts an +:unless_exist+ flag which determines whether the memcached add (when true) or set (when false) method is used to store the item in the cache and an +:expires_in+ option that specifies the time-to-live for the cached item in seconds. <ruby> ActionController::Base.cache_store = :mem_cache_store, "localhost" </ruby> -5) ActiveSupport::Cache::SynchronizedMemoryStore: Like ActiveSupport::Cache::MemoryStore but thread-safe. - +5) +ActiveSupport::Cache::SynchronizedMemoryStore+: Like +MemoryStore+ but thread-safe. <ruby> ActionController::Base.cache_store = :synchronized_memory_store </ruby> -6) ActiveSupport::Cache::CompressedMemCacheStore: Works just like the regular -MemCacheStore but uses GZip to decompress/compress on read/write. - +6) +ActiveSupport::Cache::CompressedMemCacheStore+: Works just like the regular +MemCacheStore+ but uses GZip to decompress/compress on read/write. <ruby> ActionController::Base.cache_store = :compressed_mem_cache_store, "localhost" </ruby> -7) Custom store: You can define your own cache store (new in Rails 2.1) - +7) Custom store: You can define your own cache store (new in Rails 2.1). <ruby> ActionController::Base.cache_store = MyOwnStore.new("parameter") </ruby> -+Note: +config.cache_store+ can be used in place of -+ActionController::Base.cache_store+ in your +Rails::Initializer.run+ block in -+environment.rb+ +NOTE: +config.cache_store+ can be used in place of +ActionController::Base.cache_store+ in your +Rails::Initializer.run+ block in +environment.rb+ -In addition to all of this, Rails also adds the +ActiveRecord::Base#cache_key+ -method that generates a key using the class name, +id+ and +updated_at+ timestamp (if available). +In addition to all of this, Rails also adds the +ActiveRecord::Base#cache_key+ method that generates a key using the class name, +id+ and +updated_at+ timestamp (if available). You can access these cache stores at a low level for storing queries and other objects. Here's an example: @@ -432,21 +316,11 @@ Rails.cache.read("city") # => "Duckburgh" h3. Conditional GET support -Conditional GETs are a feature of the HTTP specification that provide a way for web -servers to tell browsers that the response to a GET request hasn't changed -since the last request and can be safely pulled from the browser cache. +Conditional GETs are a feature of the HTTP specification that provide a way for web servers to tell browsers that the response to a GET request hasn't changed since the last request and can be safely pulled from the browser cache. -They work by using the +HTTP_IF_NONE_MATCH+ and +HTTP_IF_MODIFIED_SINCE+ headers -to pass back and forth both a unique content identifier and the timestamp of -when the content was last changed. If the browser makes a request where the -content identifier (etag) or last modified since timestamp matches the server’s -version then the server only needs to send back an empty response with a not -modified status. +They work by using the +HTTP_IF_NONE_MATCH+ and +HTTP_IF_MODIFIED_SINCE+ headers to pass back and forth both a unique content identifier and the timestamp of when the content was last changed. If the browser makes a request where the content identifier (etag) or last modified since timestamp matches the server’s version then the server only needs to send back an empty response with a not modified status. -It is the server's (i.e. our) responsibility to look for a last modified -timestamp and the if-none-match header and determine whether or not to send -back the full response. With conditional-get support in rails this is a pretty -easy task: +It is the server's (i.e. our) responsibility to look for a last modified timestamp and the if-none-match header and determine whether or not to send back the full response. With conditional-get support in Rails this is a pretty easy task: <ruby> class ProductsController < ApplicationController @@ -469,9 +343,7 @@ class ProductsController < ApplicationController end </ruby> -If you don't have any special response processing and are using the default -rendering mechanism (i.e. you're not using respond_to or calling render -yourself) then you’ve got an easy helper in fresh_when: +If you don't have any special response processing and are using the default rendering mechanism (i.e. you're not using respond_to or calling render yourself) then you’ve got an easy helper in fresh_when: <ruby> class ProductsController < ApplicationController @@ -481,18 +353,14 @@ class ProductsController < ApplicationController def show @product = Product.find(params[:id]) - fresh_when :last_modified => @product.published_at.utc, :etag => @article + fresh_when :last_modified => @product.published_at.utc, :etag => @product end end </ruby> h3. Advanced Caching -Along with the built-in mechanisms outlined above, a number of excellent -plugins exist to help with finer grained control over caching. These include -Chris Wanstrath's excellent cache_fu plugin (more info "here": http://errtheblog.com/posts/57-kickin-ass-w-cachefu) and Evan Weaver's -interlock plugin (more info "here": http://blog.evanweaver.com/articles/2007/12/13/better-rails-caching/). Both -of these plugins play nice with memcached and are a must-see for anyone +Along with the built-in mechanisms outlined above, a number of excellent plugins exist to help with finer grained control over caching. These include Chris Wanstrath's excellent cache_fu plugin (more info "here": http://errtheblog.com/posts/57-kickin-ass-w-cachefu) and Evan Weaver's interlock plugin (more info "here": http://blog.evanweaver.com/articles/2007/12/13/better-rails-caching/). Both of these plugins play nice with memcached and are a must-see for anyone seriously considering optimizing their caching needs. Also the new "Cache money":http://github.com/nkallen/cache-money/tree/master plugin is supposed to be mad cool. @@ -501,7 +369,7 @@ h3. References * "Scaling Rails Screencasts":http://railslab.newrelic.com/scaling-rails * "RailsEnvy, Rails Caching Tutorial, Part 1":http://www.railsenvy.com/2007/2/28/rails-caching-tutorial -* "RailsEnvy, Rails Caching Tutorial, Part 1":http://www.railsenvy.com/2007/3/20/ruby-on-rails-caching-tutorial-part-2 +* "RailsEnvy, Rails Caching Tutorial, Part 2":http://www.railsenvy.com/2007/3/20/ruby-on-rails-caching-tutorial-part-2 * "ActiveSupport::Cache documentation":http://api.rubyonrails.org/classes/ActiveSupport/Cache.html * "Rails 2.1 integrated caching tutorial":http://thewebfellas.com/blog/2008/6/9/rails-2-1-now-with-better-integrated-caching @@ -509,8 +377,10 @@ h3. References h3. Changelog "Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/10-guide-to-caching -April 1, 2009: Made a bunch of small fixes -February 22, 2009: Beefed up the section on cache_stores -December 27, 2008: Typo fixes -November 23, 2008: Incremental updates with various suggested changes and formatting cleanup -September 15, 2008: Initial version by Aditya Chadha +* May 02, 2009: Formatting cleanups +* April 26, 2009: Clean up typos in submitted patch +* April 1, 2009: Made a bunch of small fixes +* February 22, 2009: Beefed up the section on cache_stores +* December 27, 2008: Typo fixes +* November 23, 2008: Incremental updates with various suggested changes and formatting cleanup +* September 15, 2008: Initial version by Aditya Chadha diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index d97ed56eaf..2711337d43 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -194,7 +194,7 @@ Active Model currently has a single configuration setting: h3. Using Initializers -After it loads the framework plus any gems and plugins in your application, Rails turns to loading initializers. An initializer is any file of ruby code 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 plugins are loaded. +After it loads the framework plus any gems and plugins in your application, Rails turns to loading initializers. An initializer is any file of ruby code 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 plugins are loaded. 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. diff --git a/railties/guides/source/contribute.textile b/railties/guides/source/contribute.textile index 650004bd09..5087c2f385 100644 --- a/railties/guides/source/contribute.textile +++ b/railties/guides/source/contribute.textile @@ -40,7 +40,7 @@ For each completed guide, the lead contributor will receive all of the following * $200 from Caboose Rails Documentation Project. * 1 year of GitHub Micro account worth $84. -* 1 year of RPM Basic (Production performance management) for up to 10 hosts worth 12 months x $40 per host x $10 hosts = $4800. And also, savings of $45 per host per month over list price to upgrade to advanced product. +* 1 year of RPM Basic (Production performance management) for up to 10 hosts worth 12 months x $40 per host x 10 hosts = $4800. And also, savings of $45 per host per month over list price to upgrade to advanced product. h3. Rules diff --git a/railties/guides/source/contributing_to_rails.textile b/railties/guides/source/contributing_to_rails.textile index 84778ed9ee..a5912643c0 100644 --- a/railties/guides/source/contributing_to_rails.textile +++ b/railties/guides/source/contributing_to_rails.textile @@ -161,7 +161,7 @@ h4. Fork the Rails Source Code Fork Rails. You’re not going to put your patches right into the master branch, OK? This is where you need that copy of Rails that you cloned earlier. Think of a name for your new branch and run <shell> -git checkout -b my_new_branch +git checkout -b my_new_branch </shell> It doesn’t really matter what name you use, because this branch will only exist on your local computer. @@ -175,6 +175,15 @@ Now get busy and add your code to Rails (or edit the existing code). You’re on * Include tests that fail without your code, and pass with it * Update the documentation +h4. Follow the Coding Conventions + +Rails follows a simple set of coding style conventions. + +* Two spaces, no tabs +* Prefer +&&+/+||+ over +and+/+or+ +* +MyClass.my_method(my_arg)+ not +my_method( my_arg )+ or +my_method my_arg+ +* Follow the conventions you see used in the source already + h4. Sanity Check You should not be the only person who looks at the code before you submit it. You know at least one other Rails developer, right? Show them what you’re doing and ask for feedback. Doing this in private before you push a patch out publicly is the “smoke test” for a patch: if you can’t convince one other developer of the beauty of your code, you’re unlikely to convince the core team either. diff --git a/railties/guides/source/credits.erb.textile b/railties/guides/source/credits.erb.textile index b09a931fd6..49e390908f 100644 --- a/railties/guides/source/credits.erb.textile +++ b/railties/guides/source/credits.erb.textile @@ -8,15 +8,15 @@ p. We'd like to thank the following people for their tireless contributions to t <h3 class="section">Rails Documentation Team</h3> <% author('Mike Gunderloy', 'mgunderloy') do %> - Mike Gunderloy is a consultant with "ActionRails":http://www.actionrails.com and also a member of the "Rails activism team":http://rubyonrails.org/activists . He brings 25 years of experience in a variety of languages to bear on his current work with Rails. His near-daily links and other blogging can be found at "A Fresh Cup":http://afreshcup.com and he "twitters":http://twitter.com/MikeG1 too much. + Mike Gunderloy is a consultant with "ActionRails":http://www.actionrails.com. He brings 25 years of experience in a variety of languages to bear on his current work with Rails. His near-daily links and other blogging can be found at "A Fresh Cup":http://afreshcup.com and he "twitters":http://twitter.com/MikeG1 too much. <% end %> <% author('Pratik Naik', 'lifo') do %> Pratik Naik is a Ruby on Rails consultant with "ActionRails":http://www.actionrails.com and also a member of the "Rails core team":http://rubyonrails.org/core. He maintains a blog at "has_many :bugs, :through => :rails":http://m.onkey.org and has an active "twitter account":http://twitter.com/lifo. <% end %> -<% author('Xavier Noria', 'fxn', 'fxn.jpg') do %> - Xavier Noria has been around dynamic languages since 2000. He fell in love with Rails in 2005, and cofounded Rails-based software company <a href="http://www.aspgems.com">ASPgems</a> in mid-2006. Xavier is president of the <a href="http://www.srug.org/">Spanish Ruby Users Group</a> and has been involved in Rails in several ways. He enjoys combining his passion for Rails and his past life as a proofreader of math textbooks. Oh, he also "tweets":http://twitter.com/fxn! +<% author('Xavier Noria', 'fxn', 'fxn.png') do %> + Xavier has been into Rails since 2005, he is currently a Rails consultant. Xavier is president of the <a href="http://www.srug.org/">Spanish Ruby Users Group</a> and has been involved in Rails in several ways. He enjoys combining his passion for Rails and his past life as a proofreader of math textbooks. Oh, he also "tweets":http://twitter.com/fxn! <% end %> <h3 class="section">Rails Guides Designers</h3> diff --git a/railties/guides/source/debugging_rails_applications.textile b/railties/guides/source/debugging_rails_applications.textile index c059fdabf8..9c0f22724e 100644 --- a/railties/guides/source/debugging_rails_applications.textile +++ b/railties/guides/source/debugging_rails_applications.textile @@ -401,7 +401,7 @@ And then ask again for the instance_variables: true </shell> -Now +@posts+ is a included in the instance variables, because the line defining it was executed. +Now +@posts+ is included in the instance variables, because the line defining it was executed. TIP: You can also step into *irb* mode with the command +irb+ (of course!). This way an irb session will be started within the context you invoked it. But be warned: this is an experimental feature. diff --git a/railties/guides/source/form_helpers.textile b/railties/guides/source/form_helpers.textile index 22d24b0903..ac0769e219 100644 --- a/railties/guides/source/form_helpers.textile +++ b/railties/guides/source/form_helpers.textile @@ -211,9 +211,7 @@ h4. Binding a Form to an Object While this is an increase in comfort it is far from perfect. If Person has many attributes to edit then we would be repeating the name of the edited object many times. What we want to do is somehow bind a form to a model object, which is exactly what +form_for+ does. -Assume we have a controller for dealing with articles: - -articles_controller.rb: +Assume we have a controller for dealing with articles +app/controllers/articles_controller.rb+: <ruby> def new @@ -221,9 +219,7 @@ def new end </ruby> -The corresponding view using +form_for+ looks like this - -articles/new.html.erb: +The corresponding view +app/views/articles/new.html.erb+ using +form_for+ looks like this: <erb> <% form_for :article, @article, :url => { :action => "create" }, :html => {:class => "nifty_form"} do |f| %> @@ -278,7 +274,13 @@ The object yielded by +fields_for+ is a form builder like the one yielded by +fo h4. Relying on Record Identification -The Article model is directly available to users of the application, so -- following the best practices for developing with Rails -- you should declare it *a resource*. +The Article model is directly available to users of the application, so -- following the best practices for developing with Rails -- you should declare it *a resource*: + +<ruby> +map.resources :articles +</ruby> + +TIP: Declaring a resource has a number of side-affects. See "Rails Routing From the Outside In":routing.html#restful-routing-the-rails-default for more information on setting up and using resources. When dealing with RESTful resources, calls to +form_for+ can get significantly easier if you rely on *record identification*. In short, you can just pass the model instance and have Rails figure out model name and the rest: @@ -316,7 +318,7 @@ will create a form that submits to the articles controller inside the admin name form_for [:admin, :management, @article] </ruby> -For more information on Rails' routing system and the associated conventions, please see the "routing guide":./routing_outside_in.html. +For more information on Rails' routing system and the associated conventions, please see the "routing guide":routing.html. h4. How do forms with PUT or DELETE methods work? @@ -464,7 +466,7 @@ To leverage time zone support in Rails, you have to ask your users what time zon There is also +time_zone_options_for_select+ helper for a more manual (therefore more customizable) way of doing this. Read the API documentation to learn about the possible arguments for these two methods. -Rails _used_ to have a +country_select+ helper for choosing countries, but this has been extracted to the "country_select plugin":http://github.com/rails/country_select/tree/master. When using this, be aware that the exclusion or inclusion of certain names from the list can be somewhat controversial (and was the reason this functionality was extracted from rails). +Rails _used_ to have a +country_select+ helper for choosing countries, but this has been extracted to the "country_select plugin":http://github.com/rails/country_select/tree/master. When using this, be aware that the exclusion or inclusion of certain names from the list can be somewhat controversial (and was the reason this functionality was extracted from Rails). h3. Using Date and Time Form Helpers @@ -584,7 +586,7 @@ h4. Dealing with Ajax Unlike other forms making an asynchronous file upload form is not as simple as replacing +form_for+ with +remote_form_for+. With an Ajax form the serialization is done by JavaScript running inside the browser and since JavaScript cannot read files from your hard drive the file cannot be uploaded. The most common workaround is to use an invisible iframe that serves as the target for the form submission. -h3. Customising Form Builders +h3. Customizing Form Builders As mentioned previously the object yielded by +form_for+ and +fields_for+ is an instance of FormBuilder (or a subclass thereof). Form builders encapsulate the notion of displaying form elements for a single object. While you can of course write helpers for your forms in the usual way you can also subclass FormBuilder and add the helpers there. For example @@ -718,7 +720,7 @@ This will result in a +params+ hash that looks like {'person' => {'name' => 'Bob', 'address' => {'23' => {'city' => 'Paris'}, '45' => {'city' => 'London'}}}} </ruby> -Rails knows that all these inputs should be part of the person hash because you called +fields_for+ on the first form builder. By specifying an +:index+ option you're telling rails that instead of naming the inputs +person[address][city]+ it should insert that index surrounded by [] between the address and the city. If you pass an Active Record object as we did then Rails will call +to_param+ on it, which by default returns the database id. This is often useful as it is then easy to locate which Address record should be modified. You can pass numbers with some other significance, strings or even +nil+ (which will result in an array parameter being created). +Rails knows that all these inputs should be part of the person hash because you called +fields_for+ on the first form builder. By specifying an +:index+ option you're telling Rails that instead of naming the inputs +person[address][city]+ it should insert that index surrounded by [] between the address and the city. If you pass an Active Record object as we did then Rails will call +to_param+ on it, which by default returns the database id. This is often useful as it is then easy to locate which Address record should be modified. You can pass numbers with some other significance, strings or even +nil+ (which will result in an array parameter being created). To create more intricate nestings, you can specify the first part of the input name (+person[address]+ in the previous example) explicitly, for example @@ -736,7 +738,7 @@ will create inputs like As a general rule the final input name is the concatenation of the name given to +fields_for+/+form_for+, the index value and the name of the attribute. You can also pass an +:index+ option directly to helpers such as +text_field+, but it is usually less repetitive to specify this at the form builder level rather than on individual input controls. -As a shortcut you can append [] to the name and omit the +:index+ option. This is the same as specifing +:index => address+ so +As a shortcut you can append [] to the name and omit the +:index+ option. This is the same as specifying +:index => address+ so <erb> <% fields_for 'person[address][primary][]', address do |address_form| %> @@ -750,7 +752,7 @@ h3. Building Complex Forms Many apps grow beyond simple forms editing a single object. For example when creating a Person you might want to allow the user to (on the same form) create multiple address records (home, work, etc.). When later editing that person the user should be able to add, remove or amend addresses as necessary. While this guide has shown you all the pieces necessary to handle this, Rails does not yet have a standard end-to-end way of accomplishing this, but many have come up with viable approaches. These include: -* Ryan Bates' series of railscasts on "complex forms":http://railscasts.com/episodes/75 +* Ryan Bates' series of Railscasts on "complex forms":http://railscasts.com/episodes/75 * Handle Multiple Models in One Form from "Advanced Rails Recipes":http://media.pragprog.com/titles/fr_arr/multiple_models_one_form.pdf * Eloy Duran's "nested_params":http://github.com/alloy/complex-form-examples/tree/alloy-nested_params plugin * Lance Ivy's "nested_assignment":http://github.com/cainlevy/nested_assignment/tree/master plugin and "sample application":http://github.com/cainlevy/complex-form-examples/tree/cainlevy diff --git a/railties/guides/source/getting_started.textile b/railties/guides/source/getting_started.textile index 7c029762a3..5c05648f12 100644 --- a/railties/guides/source/getting_started.textile +++ b/railties/guides/source/getting_started.textile @@ -9,7 +9,7 @@ This guide covers getting up and running with Ruby on Rails. After reading it, y endprologue. -WARNING. This Guide is based on Rails 2.3. Some of the code shown here will not work in older versions of Rails. +WARNING. This Guide is based on Rails 2.3.3. Some of the code shown here will not work in other versions of Rails. h3. This Guide Assumes @@ -19,22 +19,22 @@ This guide is designed for beginners who want to get started with a Rails applic * The "RubyGems":http://rubyforge.org/frs/?group_id=126 packaging system * A working installation of "SQLite":http://www.sqlite.org (preferred), "MySQL":http://www.mysql.com, or "PostgreSQL":http://www.postgresql.org -It is highly recommended that you *familiarize yourself with Ruby before diving into Rails*. You will find it much easier to follow what’s going on with a Rails application if you understand basic Ruby syntax. Rails isn’t going to magically revolutionize the way you write web applications if you have no experience with the language it uses. There are some good free resources on the internet for learning Ruby, including: +It is highly recommended that you *familiarize yourself with Ruby before diving into Rails*. You will find it much easier to follow what's going on with a Rails application if you understand basic Ruby syntax. Rails isn't going to magically revolutionize the way you write web applications if you have no experience with the language it uses. There are some good free resources on the internet for learning Ruby, including: -* "Mr. Neighborly’s Humble Little Ruby Book":http://www.humblelittlerubybook.com +* "Mr. Neighborly's Humble Little Ruby Book":http://www.humblelittlerubybook.com * "Programming Ruby":http://www.rubycentral.com/book -* "Why’s (Poignant) Guide to Ruby":http://poignantguide.net/ruby/ +* "Why's (Poignant) Guide to Ruby":http://poignantguide.net/ruby/ h3. What is Rails? -Rails is a web development framework written in the Ruby language. It is designed to make programming web applications easier by making several assumptions about what every developer needs to get started. It allows you to write less code while accomplishing more than many other languages and frameworks. Longtime Rails developers also report that it makes web application development more fun. +Rails is a web 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 code while accomplishing more than many other languages and frameworks. Longtime Rails developers also report that it makes web application development more fun. -Rails is opinionated software. That is, it assumes that there is a best way to do things, and it’s designed to encourage that best way - and in some cases to discourage alternatives. If you learn "The Rails Way" you’ll probably discover a tremendous increase in productivity. If you persist in bringing old habits from other languages to your Rails development, and trying to use patterns you learned elsewhere, you may have a less happy experience. +Rails is opinionated software. That is, it assumes that there is a best way to do things, and it's designed to encourage that best way - and in some cases to discourage alternatives. If you learn "The Rails Way" you'll probably discover a tremendous increase in productivity. If you persist in bringing old habits from other languages to your Rails development, and trying to use patterns you learned elsewhere, you may have a less happy experience. The Rails philosophy includes several guiding principles: -* DRY - "Don’t Repeat Yourself" - suggests that writing the same code over and over again is a bad thing. -* Convention Over Configuration - means that Rails makes assumptions about what you want to do and how you’re going to do it, rather than letting you tweak every little thing through endless configuration files. +* DRY - "Don't Repeat Yourself" - suggests that writing the same code over and over again is a bad thing. +* Convention Over Configuration - means that Rails makes assumptions about what you want to do and how you're going to do it, rather than letting you tweak every little thing through endless configuration files. * REST is the best pattern for web applications - organizing your application around resources and standard HTTP verbs is the fastest way to go. h4. The MVC Architecture @@ -47,7 +47,7 @@ Rails is organized around the Model, View, Controller architecture, usually just h5. Models -A model represents the information (data) of the application and the rules to manipulate that data. In the case of Rails, models are primarily used for managing the rules of interaction with a corresponding database table. In most cases, one table in your database will correspond to one model in your application. The bulk of your application’s business logic will be concentrated in the models. +A model represents the information (data) of the application and the rules to manipulate that data. In the case of Rails, models are primarily used for managing the rules of interaction with a corresponding database table. In most cases, one table in your database will correspond to one model in your application. The bulk of your application's business logic will be concentrated in the models. h5. Views @@ -87,7 +87,7 @@ Action Mailer is a framework for building e-mail services. You can use Action Ma h5. Active Resource -Active Resource provides a framework for managing the connection between business objects an RESTful web services. It implements a way to map web-based resources to local objects with CRUD semantics. +Active Resource provides a framework for managing the connection between business objects and RESTful web services. It implements a way to map web-based resources to local objects with CRUD semantics. h5. Railties @@ -99,7 +99,7 @@ Active Support is an extensive collection of utility classes and standard Ruby l h4. REST -The foundation of the RESTful architecture is generally considered to be Roy Fielding’s doctoral thesis, "Architectural Styles and the Design of Network-based Software Architectures":http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm. Fortunately, you need not read this entire document to understand how REST works in Rails. REST, an acronym for Representational State Transfer, boils down to two main principles for our purposes: +The foundation of the RESTful architecture is generally considered to be Roy Fielding's doctoral thesis, "Architectural Styles and the Design of Network-based Software Architectures":http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm. Fortunately, you need not read this entire document to understand how REST works in Rails. REST, an acronym for Representational State Transfer, boils down to two main principles for our purposes: * Using resource identifiers (which, for the purposes of discussion, you can think of as URLs) to represent resources * Transferring representations of the state of that resource between system components. @@ -110,7 +110,7 @@ For example, to a Rails application a request such as this: would be understood to refer to a photo resource with the ID of 17, and to indicate a desired action - deleting that resource. REST is a natural style for the architecture of web applications, and Rails makes it even more natural by using conventions to shield you from some of the RESTful complexities and browser quirks. -If you’d like more details on REST as an architectural style, these resources are more approachable than Fielding’s thesis: +If you'd like more details on REST as an architectural style, these resources are more approachable than Fielding's thesis: * "A Brief Introduction to REST":http://www.infoq.com/articles/rest-introduction by Stefan Tilkov * "An Introduction to REST":http://bitworking.org/news/373/An-Introduction-to-REST (video tutorial) by Joe Gregorio @@ -119,7 +119,7 @@ If you’d like more details on REST as an architectural style, these resources h3. Creating a New Rails Project -If you follow 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. +If you follow 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. h4. Installing Rails @@ -131,8 +131,10 @@ $ gem install rails NOTE. There are some special circumstances in which you might want to use an alternate installation strategy: -* If you’re working on Windows, you may find it easier to install Instant Rails. Be aware, though, that "Instant Rails":http://instantrails.rubyforge.org/wiki/wiki.pl releases tend to lag seriously behind the actual Rails version. Also, you will find that Rails development on Windows is overall less pleasant than on other operating systems. If at all possible, we suggest that you install a Linux virtual machine and use that for Rails development, instead of using Windows. -* If you want to keep up with cutting-edge changes to Rails, you’ll want to clone the "Rails source code":http://github.com/rails/rails/tree/master from github. This is not recommended as an option for beginners, though. +* If you're working on Windows, you may find it easier to install Instant Rails. Be aware, though, that "Instant Rails":http://instantrails.rubyforge.org/wiki/wiki.pl releases tend to lag seriously behind the actual Rails version. Also, you will find that Rails development on Windows is overall less pleasant than on other operating systems. If at all possible, we suggest that you install a Linux virtual machine and use that for Rails development, instead of using Windows. +* If you want to keep up with cutting-edge changes to Rails, you'll want to clone the "Rails source code":http://github.com/rails/rails/tree/master from github. This is not recommended as an option for beginners, though. + +WARNING. As of mid-2009, cloning the master branch will get you preliminary Rails 3.0 code. To follow along with this guide, you should clone the 2-3-stable branch instead. h4. Creating the Blog Application @@ -148,13 +150,13 @@ This will create a Rails application that uses a SQLite database for data storag $ rails blog -d mysql </shell> -And if you’re using PostgreSQL for data storage, run this command: +And if you're using PostgreSQL for data storage, run this command: <shell> $ rails blog -d postgresql </shell> -TIP. You can see all of the switches that the Rails application builder accepts by running <tt>rails -h</tt>. +TIP: You can see all of the switches that the Rails application builder accepts by running <tt>rails -h</tt>. After you create the blog application, switch to its folder to continue work directly in that application: @@ -162,7 +164,7 @@ After you create the blog application, switch to its folder to continue work dir $ 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 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: +In any case, Rails will create a folder in your working directory called <tt>blog</tt>. Open up that folder and explore its contents. Most of the work in this tutorial will happen in the <tt>app/</tt> folder, but here's a basic rundown on the function of each folder that Rails creates in a new application by default: |_.File/Folder|_.Purpose| |README|This is a brief instruction manual for your application. Use it to tell others what your application does, how to set it up, and so on.| @@ -259,7 +261,7 @@ One of the traditional places to start with a new language is by getting some te $ script/generate controller home index </shell> -TIP. If you're on Windows, or your Ruby is set up in some non-standard fashion, you may need to explicitly pass Rails +script+ commands to Ruby: +ruby script/generate controller home index+. +TIP: If you're on Windows, or your Ruby is set up in some non-standard fashion, you may need to explicitly pass Rails +script+ commands to Ruby: +ruby script/generate controller home index+. Rails will create several files for you, including +app/views/home/index.html.erb+. This is the template that will be used to display the results of the +index+ action (method) in the +home+ controller. Open this file in your text editor and edit it to contain a single line of code: @@ -279,7 +281,7 @@ This will fire up an instance of the Mongrel web server by default (Rails can al !images/rails_welcome.png(Welcome Aboard screenshot)! -TIP. To stop the web server, hit Ctrl+C in the terminal window where it's running. In development mode, Rails does not generally require you to stop the server; changes you make in files will be automatically picked up by the server. +TIP: To stop the web server, hit Ctrl+C in the terminal window where it's running. In development mode, Rails does not generally require you to stop the server; changes you make in files will be automatically picked up by the server. The "Welcome Aboard" page is the _smoke test_ for a new Rails application: it makes sure that you have your software configured correctly enough to serve a page. To view the page you just created, navigate to +http://localhost:3000/home/index+. @@ -383,7 +385,7 @@ NOTE: Because you're working in the development environment by default, this com h4. Adding a Link -To hook the posts up to the home page you've already created, you can add a link to the home page. Open +/app/views/home/index.html.erb+ and modify it as follows: +To hook the posts up to the home page you've already created, you can add a link to the home page. Open +app/views/home/index.html.erb+ and modify it as follows: <code lang="ruby"> <h1>Hello, Rails!</h1> @@ -400,7 +402,7 @@ Now you're ready to start working with posts. To do that, navigate to +http://lo This is the result of Rails rendering the +index+ view of your posts. There aren't currently any posts in the database, but if you click the +New Post+ link you can create one. After that, you'll find that you can edit posts, look at their details, or destroy them. All of the logic and HTML to handle this was built by the single +script/generate scaffold+ command. -TIP. In development mode (which is what you're working in by default), Rails reloads your application with every browser request, so there's no need to stop and restart the web server. +TIP: In development mode (which is what you're working in by default), Rails reloads your application with every browser request, so there's no need to stop and restart the web server. Congratulations, you're riding the rails! Now it's time to see how it all works. @@ -461,7 +463,7 @@ The easiest place to start looking at functionality is with the code that lists <ruby> def index - @posts = Post.find(:all) + @posts = Post.all respond_to do |format| format.html # index.html.erb @@ -486,7 +488,7 @@ The +respond_to+ block handles both HTML and XML calls to this action. If you br <th>Content</th> </tr> -<% for post in @posts %> +<% @posts.each do |post| %> <tr> <td><%=h post.name %></td> <td><%=h post.title %></td> @@ -508,9 +510,9 @@ This view iterates over the contents of the +@posts+ array to display content an * +h+ is a Rails helper method to sanitize displayed data, preventing cross-site scripting attacks * +link_to+ builds a hyperlink to a particular destination -* +edit_post_path+ is a helper that Rails provides as part of RESTful routing. You’ll see a variety of these helpers for the different actions that the controller includes. +* +edit_post_path+ is a helper that Rails provides as part of RESTful routing. You'll see a variety of these helpers for the different actions that the controller includes. -TIP. For more details on the rendering process, see "Layouts and Rendering in Rails":layouts_and_rendering.html. +TIP: For more details on the rendering process, see "Layouts and Rendering in Rails":layouts_and_rendering.html. h4. Customizing the Layout @@ -582,7 +584,7 @@ The +new.html.erb+ view displays this empty Post to the user: <%= link_to 'Back', posts_path %> </erb> -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 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 having your write raw HTML because the code is more succinct, and because it explicitly ties the form to a particular model instance. +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 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 having you write raw HTML because the code is more succinct, and because it explicitly ties the form to a particular model instance. TIP: If you need to create an HTML form that displays arbitrary fields, not tied 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. @@ -609,7 +611,7 @@ end The +create+ action instantiates a new Post object from the data supplied by the user on the form, which Rails makes available in the +params+ hash. After saving the new post, it uses +flash[:notice]+ to create an informational message for the user, and redirects to the show action for the post. If there's any problem, the +create+ action just shows the +new+ view a second time, with any error messages. -Rails provides the +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 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." +Rails provides the +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 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." h4. Showing an Individual Post @@ -710,7 +712,7 @@ 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 +edit+ to correct them. -NOTE. Sharp-eyed readers will have noticed that the +form_for+ declaration is identical for the +new+ and +edit+ views. Rails generates different code for the two forms because it's smart enough to notice that in the one case it's being passed a new record that has never been saved, and in the other case an existing record that has already been saved to the database. In a production Rails application, you would ordinarily eliminate this duplication by moving identical code to a _partial template_, which you could then include in both parent templates. But the scaffold generator tries not to make too many assumptions, and generates code that’s easy to modify if you want different forms for +create+ and +edit+. +NOTE. Sharp-eyed readers will have noticed that the +form_for+ declaration is identical for the +new+ and +edit+ views. Rails generates different code for the two forms because it's smart enough to notice that in the one case it's being passed a new record that has never been saved, and in the other case an existing record that has already been saved to the database. In a production Rails application, you would ordinarily eliminate this duplication by moving identical code to a _partial template_, which you could then include in both parent templates. But the scaffold generator tries not to make too many assumptions, and generates code that's easy to modify if you want different forms for +create+ and +edit+. h4. Destroying a Post @@ -732,7 +734,7 @@ The +destroy+ method of an Active Record model instance removes the correspondin h3. DRYing up the Code -At this point, it’s worth looking at some of the tools that Rails provides to eliminate duplication in your code. In particular, you can use _partials_ to clean up duplication in views and _filters_ to help with duplication in controllers. +At this point, it's worth looking at some of the tools that Rails provides to eliminate duplication in your code. In particular, you can use _partials_ to clean up duplication in views and _filters_ to help with duplication in controllers. h4. Using Partials to Eliminate View Duplication @@ -740,13 +742,13 @@ As you saw earlier, the scaffold-generated views for the +new+ and +edit+ action <tt>new.html.erb</tt>: -<html> +<erb> <h1>New post</h1> <%= render :partial => "form" %> <%= link_to 'Back', posts_path %> -</html> +</erb> <tt>edit.html.erb</tt>: @@ -785,11 +787,11 @@ As you saw earlier, the scaffold-generated views for the +new+ and +edit+ action Now, when Rails renders the +new+ or +edit+ view, it will insert the +_form+ partial at the indicated point. Note the naming convention for partials: if you refer to a partial named +form+ inside of a view, the corresponding file is +_form.html.erb+, with a leading underscore. -For more information on partials, refer to the "Layouts and Rending in Rails":layouts_and_rendering.html guide. +For more information on partials, refer to the "Layouts and Rendering in Rails":layouts_and_rendering.html#using-partials guide. h4. Using Filters to Eliminate Controller Duplication -At this point, if you look at the controller for posts, you’ll see some duplication: +At this point, if you look at the controller for posts, you'll see some duplication: <ruby> class PostsController < ApplicationController @@ -815,7 +817,7 @@ class PostsController < ApplicationController end </ruby> -Four instances of the exact same line of code doesn’t seem very DRY. Rails provides _filters_ as a way to address this sort of repeated code. In this case, you can DRY things up by using a +before_filter+: +Four instances of the exact same line of code doesn't seem very DRY. Rails provides _filters_ as a way to address this sort of repeated code. In this case, you can DRY things up by using a +before_filter+: <ruby> class PostsController < ApplicationController @@ -837,10 +839,10 @@ class PostsController < ApplicationController # ... end - private - def find_post - @post = Post.find(params[:id]) - end +private + def find_post + @post = Post.find(params[:id]) + end end </ruby> @@ -944,7 +946,7 @@ map.resources :posts, :has_many => :comments This creates +comments+ as a _nested resource_ within +posts+. This is another part of capturing the hierarchical relationship that exists between posts and comments. -TIP: For more information on routing, see the "Rails Routing from the Outside In":routing_outside_in.html guide. +TIP: For more information on routing, see the "Rails Routing from the Outside In":routing.html guide. h4. Generating a Controller @@ -954,7 +956,7 @@ With the model in hand, you can turn your attention to creating a matching contr $ script/generate controller Comments index show new edit </shell> -This creates seven files: +This creates eight files: * +app/controllers/comments_controller.rb+ - The controller * +app/helpers/comments_helper.rb+ - A view helper file @@ -963,6 +965,7 @@ This creates seven files: * +app/views/comments/new.html.erb+ - The view for the new action * +app/views/comments/edit.html.erb+ - The view for the edit action * +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 The controller will be generated with empty methods and views for each action that you specified in the call to +script/generate controller+: @@ -987,23 +990,21 @@ You'll need to flesh this out with code to actually process requests appropriate <ruby> class CommentsController < ApplicationController + before_filter :find_post + def index - @post = Post.find(params[:post_id]) @comments = @post.comments end def show - @post = Post.find(params[:post_id]) @comment = @post.comments.find(params[:id]) end def new - @post = Post.find(params[:post_id]) @comment = @post.comments.build end def create - @post = Post.find(params[:post_id]) @comment = @post.comments.build(params[:comment]) if @comment.save redirect_to post_comment_url(@post, @comment) @@ -1013,12 +1014,10 @@ class CommentsController < ApplicationController end def edit - @post = Post.find(params[:post_id]) @comment = @post.comments.find(params[:id]) end def update - @post = Post.find(params[:post_id]) @comment = Comment.find(params[:id]) if @comment.update_attributes(params[:comment]) redirect_to post_comment_url(@post, @comment) @@ -1028,14 +1027,14 @@ class CommentsController < ApplicationController end def destroy - @post = Post.find(params[:post_id]) @comment = Comment.find(params[:id]) @comment.destroy + redirect_to post_comments_path(@post) + end - respond_to do |format| - format.html { redirect_to post_comments_path(@post) } - format.xml { head :ok } - end +private + def find_post + @post = Post.find(params[:post_id]) end end @@ -1198,6 +1197,18 @@ As a next step, I'll modify the +views/posts/show.html.erb+ view to show the com Note that each post has its own individual comments collection, accessible as +@post.comments+. That's a consequence of the declarative associations in the models. Path helpers such as +post_comments_path+ come from the nested route declaration in +config/routes.rb+. +h4. Deleting Associated Objects + +If you decide at some point to delete a post, you likely want to delete the comments associated with that post as well. You can do so by taking advantage of the association option +dependent+. All you need to do is modify the +post.rb+ as follows: + +<ruby> +class Post < ActiveRecord::Base + validates_presence_of :name, :title + validates_length_of :title, :minimum => 5 + has_many :comments, :dependent => :destroy +end +</ruby> + h3. Building a Multi-Model Form Comments and posts are edited on two separate forms - which makes sense, given the flow of this mini-application. But what if you want to edit more than one thing on a single form? Rails 2.3 offers new support for nested forms. Let's add support for giving each post multiple tags, right in the form where you create the post. First, create a new model to hold the tags: @@ -1212,7 +1223,7 @@ Run the migration to create the database table: $ rake db:migrate </shell> -Next, edit the +post.rb+ file to create the other side of the association, and to tell Rails that you intend to edit tags via posts: +Next, edit the +post.rb+ file to create the other side of the association, and to tell Rails (via the +accepts_nested_attributes+ macro) that you intend to edit tags via posts: <ruby> class Post < ActiveRecord::Base @@ -1221,7 +1232,7 @@ class Post < ActiveRecord::Base has_many :comments has_many :tags - accepts_nested_attributes_for :tags, :allow_destroy => :true , + accepts_nested_attributes_for :tags, :allow_destroy => :true, :reject_if => proc { |attrs| attrs.all? { |k, v| v.blank? } } end </ruby> @@ -1240,7 +1251,7 @@ You'll also need to modify +views/posts/_form.html.erb+ to include the tags: <%= post_form.text_field :name %> </p> <p> - <%= post_form.label :title, "title" %><br /> + <%= post_form.label :title, "Title" %><br /> <%= post_form.text_field :title %> </p> <p> @@ -1277,17 +1288,19 @@ Now that you've seen your first Rails application, you should feel free to updat * The "Ruby On Rails guides":http://guides.rubyonrails.org * The "Ruby on Rails mailing list":http://groups.google.com/group/rubyonrails-talk -* The #rubyonrails channel on irc.freenode.net +* 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: -* Running +rake doc:guides+ will put a full copy of the Rails Guides in the +/doc/guides+ folder of your application. Open +/doc/guides/index.html+ in your web browser to explore the Guides. -* Running +rake doc:rails+ will put a full copy of the API documentation for Rails in the +/doc/api+ folder of your application. Open +/doc/api/index.html+ in your web browser to explore the API documentation. +* Running +rake doc:guides+ will put a full copy of the Rails Guides in the +doc/guides+ folder of your application. Open +doc/guides/index.html+ in your web browser to explore the Guides. +* Running +rake doc:rails+ will put a full copy of the API documentation for Rails in the +doc/api+ folder of your application. Open +doc/api/index.html+ in your web browser to explore the API documentation. h3. Changelog "Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/2 +* July 18, 2009: Minor cleanup in anticipation of Rails 2.3.3 by "Mike Gunderloy":credits.html#mgunderloy * February 1, 2009: Updated for Rails 2.3 by "Mike Gunderloy":credits.html#mgunderloy * November 3, 2008: Formatting patch from Dave Rothlisberger * November 1, 2008: First approved version by "Mike Gunderloy":credits.html#mgunderloy diff --git a/railties/guides/source/i18n.textile b/railties/guides/source/i18n.textile index 103ccb1c7a..b588656821 100644 --- a/railties/guides/source/i18n.textile +++ b/railties/guides/source/i18n.textile @@ -97,7 +97,7 @@ The *translations load path* (+I18n.load_path+) is just a Ruby Array of paths to NOTE: The backend will lazy-load these translations when a translation is looked up for the first time. This makes it possible to just swap the backend with something else even after translations have already been announced. -The default +environment.rb+ files has instruction how to add locales from another directory and how to set a different default locale. Just uncomment and edit the specific lines. +The default +environment.rb+ files has instructions on how to add locales from another directory and how to set a different default locale. Just uncomment and edit the specific lines. <ruby> # The internationalization framework can be changed @@ -114,7 +114,7 @@ For the sake of completeness, let's mention that if you do not want to use the + To tell the I18n library where it can find your custom translation files you can specify the load path anywhere in your application - just make sure it gets run before any translations are actually looked up. You might also want to change the default locale. The simplest thing possible is to put the following into an initializer: <ruby> -# in config/initializer/locale.rb +# in config/initializers/locale.rb # tell the I18n library where to find your translations I18n.load_path << Dir[ File.join(RAILS_ROOT, 'lib', 'locale', @@ -142,61 +142,28 @@ def set_locale end </ruby> -This requires you to pass the locale as a URL query parameter as in +http://example.com/books?locale=pt+. (This is, for example, Google's approach.) So +http://localhost:3000?locale=pt+ will load the Portugese localization, whereas +http://localhost:3000?locale=de+ would load the German localization, and so on. You may skip the next section and head over to the *Internationalize your application* section, if you want to try things out by manually placing the locale in the URL and reloading the page. +This requires you to pass the locale as a URL query parameter as in +http://example.com/books?locale=pt+. (This is, for example, Google's approach.) So +http://localhost:3000?locale=pt+ will load the Portuguese localization, whereas +http://localhost:3000?locale=de+ would load the German localization, and so on. You may skip the next section and head over to the *Internationalize your application* section, if you want to try things out by manually placing the locale in the URL and reloading the page. Of course, you probably don't want to manually include the locale in every URL all over your application, or want the URLs look differently, e.g. the usual +http://example.com/pt/books+ versus +http://example.com/en/books+. Let's discuss the different options you have. -IMPORTANT: The following examples rely on having available locales loaded into your application as an array of strings like +["en", "es", "gr"]+. This is not included in the current version of Rails 2.2 -- the forthcoming Rails version 2.3 will contain the easy accessor +available_locales+. (See "this commit":http://github.com/svenfuchs/i18n/commit/411f8fe7c8f3f89e9b6b921fa62ed66cb92f3af4 and background at "Rails I18n Wiki":http://rails-i18n.org/wiki/pages/i18n-available_locales.) - -So, for having available locales easily accessible in Rails 2.2, we have to include this support manually in an initializer, like this: - -<ruby> -# config/initializers/available_locales.rb -# -# Get loaded locales conveniently -# See http://rails-i18n.org/wiki/pages/i18n-available_locales -module I18n - class << self - def available_locales; backend.available_locales; end - end - module Backend - class Simple - def available_locales; translations.keys.collect { |l| l.to_s }.sort; end - end - end -end - -# You need to "force-initialize" loaded locales -I18n.backend.send(:init_translations) - -AVAILABLE_LOCALES = I18n.backend.available_locales -RAILS_DEFAULT_LOGGER.debug "* Loaded locales: #{AVAILABLE_LOCALES.inspect}" -</ruby> - -You can then wrap the constant for easy access in ApplicationController: - -<ruby> -class ApplicationController < ActionController::Base - def available_locales; AVAILABLE_LOCALES; end -end -</ruby> - h4. Setting the Locale from the Domain Name One option you have is to set the locale from the domain name where your application runs. For example, we want +www.example.com+ to load the English (or default) locale, and +www.example.es+ to load the Spanish locale. Thus the _top-level domain name_ is used for locale setting. This has several advantages: -* The locale is an _obvious_ part of the URL -* People intuitively grasp in which language the content will be displayed -* It is very trivial to implement in Rails -* Search engines seem to like that content in different languages lives at different, inter-linked domains +* The locale is an _obvious_ part of the URL. +* People intuitively grasp in which language the content will be displayed. +* It is very trivial to implement in Rails. +* Search engines seem to like that content in different languages lives at different, inter-linked domains. -You can implement it like this in your ApplicationController: +You can implement it like this in your +ApplicationController+: <ruby> before_filter :set_locale + def set_locale I18n.locale = extract_locale_from_uri end + # Get locale from top-level domain or return nil if such locale is not available # You have to put something like: # 127.0.0.1 application.com @@ -205,7 +172,7 @@ end # in your /etc/hosts file to try this out locally def extract_locale_from_tld parsed_locale = request.host.split('.').last - (available_locales.include? parsed_locale) ? parsed_locale : nil + I18n.available_locales.include?(parsed_locale.to_sym) ? parsed_locale : nil end </ruby> @@ -218,7 +185,7 @@ We can also set the locale from the _subdomain_ in a very similar way: # in your /etc/hosts file to try this out locally def extract_locale_from_subdomain parsed_locale = request.subdomains.first - (available_locales.include? parsed_locale) ? parsed_locale : nil + I18n.available_locales.include?(parsed_locale.to_sym) ? parsed_locale : nil end </ruby> @@ -276,11 +243,11 @@ map.dashboard '/:locale', :controller => "dashboard" Do take special care about the *order of your routes*, so this route declaration does not "eat" other ones. (You may want to add it directly before the +map.root+ declaration.) -IMPORTANT: This solution has currently one rather big *downside*. Due to the _default_url_options_ implementation, you have to pass the +:id+ option explicitely, like this: +link_to 'Show', book_url(:id => book)+ and not depend on Rails' magic in code like +link_to 'Show', book+. If this should be a problem, have a look at two plugins which simplify work with routes in this way: Sven Fuchs's "routing_filter":http://github.com/svenfuchs/routing-filter/tree/master and Raul Murciano's "translate_routes":http://github.com/raul/translate_routes/tree/master. See also the page "How to encode the current locale in the URL":http://rails-i18n.org/wiki/pages/how-to-encode-the-current-locale-in-the-url in the Rails i18n Wiki. +IMPORTANT: This solution has currently one rather big *downside*. Due to the _default_url_options_ implementation, you have to pass the +:id+ option explicitly, like this: +link_to 'Show', book_url(:id => book)+ and not depend on Rails' magic in code like +link_to 'Show', book+. If this should be a problem, have a look at two plugins which simplify work with routes in this way: Sven Fuchs's "routing_filter":http://github.com/svenfuchs/routing-filter/tree/master and Raul Murciano's "translate_routes":http://github.com/raul/translate_routes/tree/master. See also the page "How to encode the current locale in the URL":http://rails-i18n.org/wiki/pages/how-to-encode-the-current-locale-in-the-url in the Rails i18n Wiki. h4. Setting the Locale from the Client Supplied Information -In specific cases, it would make sense to set the locale from client-supplied information, i.e. not from the URL. This information may come for example from the users' prefered language (set in their browser), can be based on the users' geographical location inferred from their IP, or users can provide it simply by choosing the locale in your application interface and saving it to their profile. This approach is more suitable for web-based applications or services, not for websites -- see the box about _sessions_, _cookies_ and RESTful architecture above. +In specific cases, it would make sense to set the locale from client-supplied information, i.e. not from the URL. This information may come for example from the users' preferred language (set in their browser), can be based on the users' geographical location inferred from their IP, or users can provide it simply by choosing the locale in your application interface and saving it to their profile. This approach is more suitable for web-based applications or services, not for websites -- see the box about _sessions_, _cookies_ and RESTful architecture above. h5. Using +Accept-Language+ @@ -305,7 +272,7 @@ Of course, in a production environment you would need much more robust code, and h5. Using GeoIP (or Similar) Database -Another way of choosing the locale from client information would be to use a database for mapping the client IP to the region, such as "GeoIP Lite Country":http://www.maxmind.com/app/geolitecountry. The mechanics of the code would be very similar to the code above -- you would need to query the database for the user's IP, and look up your prefered locale for the country/region/city returned. +Another way of choosing the locale from client information would be to use a database for mapping the client IP to the region, such as "GeoIP Lite Country":http://www.maxmind.com/app/geolitecountry. The mechanics of the code would be very similar to the code above -- you would need to query the database for the user's IP, and look up your preferred locale for the country/region/city returned. h5. User Profile @@ -315,7 +282,7 @@ h3. Internationalizing your Application OK! Now you've initialized I18n support for your Ruby on Rails application and told it which locale to use and how to preserve it between requests. With that in place, you're now ready for the really interesting stuff. -Let's _internationalize_ our application, i.e. abstract every locale-specific parts, and then _localize_ it, i.e. provide neccessary translations for these abstracts. +Let's _internationalize_ our application, i.e. abstract every locale-specific parts, and then _localize_ it, i.e. provide necessary translations for these abstracts. You most probably have something like this in one of your applications: @@ -365,12 +332,12 @@ NOTE: Rails adds a +t+ (+translate+) helper method to your views so that you do So let's add the missing translations into the dictionary files (i.e. do the "localization" part): <ruby> -# config/locale/en.yml +# config/locales/en.yml en: hello_world: Hello World hello_flash: Hello Flash -# config/locale/pirate.yml +# config/locales/pirate.yml pirate: hello_world: Ahoy World hello_flash: Ahoy Flash @@ -378,7 +345,7 @@ pirate: There you go. Because you haven't changed the default_locale, I18n will use English. Your application now shows: -!images/i18n/demo_translated_en.png(rails i18n demo translated to english)! +!images/i18n/demo_translated_en.png(rails i18n demo translated to English)! And when you change the URL to pass the pirate locale (+http://localhost:3000?locale=pirate+), you'll get: @@ -386,7 +353,7 @@ And when you change the URL to pass the pirate locale (+http://localhost:3000?lo 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 prefered 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.) +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. Adding Date/Time Formats @@ -402,7 +369,7 @@ OK! Now let's add a timestamp to the view, so we can demo the *date/time localiz And in our pirate translations file let's add a time format (it's already there in Rails' defaults for English): <ruby> -# config/locale/pirate.yml +# config/locales/pirate.yml pirate: time: formats: @@ -413,7 +380,7 @@ So that would give you: !images/i18n/demo_localized_pirate.png(rails i18n demo localized time to pirate)! -TIP: Right now you might need to add some more date/time formats in order to make the I18n backend work as expected (at least for the 'pirate' locale). Of course, there's a great chance that somebody already did all the work by *translating Rails's defaults for your locale*. See the "rails-i18n repository at Github":http://github.com/svenfuchs/rails-i18n/tree/master/rails/locale for an archive of various locale files. When you put such file(s) in +config/locale/+ directory, they will automatically be ready for use. +TIP: Right now you might need to add some more date/time formats in order to make the I18n backend work as expected (at least for the 'pirate' locale). Of course, there's a great chance that somebody already did all the work by *translating Rails' defaults for your locale*. See the "rails-i18n repository at Github":http://github.com/svenfuchs/rails-i18n/tree/master/rails/locale for an archive of various locale files. When you put such file(s) in +config/locales/+ directory, they will automatically be ready for use. h4. Localized Views @@ -425,7 +392,7 @@ h4. Organization of Locale Files When you are using the default SimpleStore shipped with the i18n library, dictionaries are stored in plain-text files on the disc. Putting translations for all parts of your application in one file per locale could be hard to manage. You can store these files in a hierarchy which makes sense to you. -For example, your +config/locale+ directory could look like this: +For example, your +config/locales+ directory could look like this: <pre> |-defaults @@ -463,7 +430,7 @@ Do check the "Rails i18n Wiki":http://rails-i18n.org/wiki for list of tools avai h3. Overview of the I18n API Features -You should have good understanding of using the i18n library now, knowing all neccessary aspects of internationalizing a basic Rails application. In the following chapters, we'll cover it's features in more depth. +You should have good understanding of using the i18n library now, knowing all necessary aspects of internationalizing a basic Rails application. In the following chapters, we'll cover it's features in more depth. Covered are features like these: @@ -686,7 +653,7 @@ en: # will translate User attribute "login" as "Handle" </ruby> -Then +User.human_name+ will return "Dude" and +User.human_attribute_name(:login)+ will return "Handle". +Then +User.human_name+ will return "Dude" and +User.human_attribute_name("login")+ will return "Handle". h5. Error Message Scopes diff --git a/railties/guides/source/index.erb.textile b/railties/guides/source/index.erb.textile index 4c8dd65a04..851d91da2f 100644 --- a/railties/guides/source/index.erb.textile +++ b/railties/guides/source/index.erb.textile @@ -1,5 +1,5 @@ <% content_for :header_section do %> -h2. Ruby on Rails guides +h2. Ruby on Rails Guides These guides are designed to make you immediately productive with Rails, and to help you understand how all of the pieces fit together. There are two different versions of the Guides site, and you should be sure to use the one that applies to your situation: diff --git a/railties/guides/source/layouts_and_rendering.textile b/railties/guides/source/layouts_and_rendering.textile index 809d2b2172..0cee413ac3 100644 --- a/railties/guides/source/layouts_and_rendering.textile +++ b/railties/guides/source/layouts_and_rendering.textile @@ -204,7 +204,7 @@ TIP: You don't need to call +to_xml+ on the object that you want to render. If y h5. Rendering Vanilla JavaScript -Rails can render vanilla JavaScript (as an alternative to using +update+ with n +.rjs+ file): +Rails can render vanilla JavaScript (as an alternative to using +update+ with an +.rjs+ file): <ruby> render :js => "alert('Hello Rails');" @@ -266,7 +266,7 @@ render :xml => photo, :location => photo_url(photo) h5. Finding Layouts -To find the current layout, Rails first looks for a file in +app/views/layouts+ with the same base name as the controller. For example, rendering actions from the +PhotosController+ class will use +/app/views/layouts/photos.html.erb+ (or +app/views/layouts/photos.builder+). If there is no such controller-specific layout, Rails will use +/app/views/layouts/application.html.erb+ ot +/app/views/layouts/application.builder+. If there is no +.erb+ layout, Rails will use a +.builder+ layout if one exists. Rails also provides several ways to more precisely assign specific layouts to individual controllers and actions. +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 @@ -418,15 +418,15 @@ def show end </ruby> -Note that the implicit render done by ActionController detects if +render+ has been called, and thus avoids this error. So this code will work with problems: +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: <ruby> - def show - @book = Book.find(params[:id]) - if @book.special? - render :action => "special_show" - end +def show + @book = Book.find(params[:id]) + if @book.special? + render :action => "special_show" end +end </ruby> This will render a book with +special?+ set with the +special_show+ template, while other books will render with the default +show+ template. @@ -447,7 +447,7 @@ redirect_to :back h5. Getting a Different Redirect Status Code -Rails uses HTTP status code 302 (permanent redirect) when you call +redirect_to+. If you'd like to use a different status code (perhaps 301, temporary redirect), you can do so by using the +:status+ option: +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: <ruby> redirect_to photos_path, :status => 301 @@ -519,10 +519,10 @@ h4. Asset Tags Asset tags provide methods for generating HTML that links views to assets like images, javascript, stylesheets, and feeds. There are four types of include tag: -* auto_discovery_link_tag -* javascript_include_tag -* stylesheet_link_tag -* image_tag +* +auto_discovery_link_tag+ +* +javascript_include_tag+ +* +stylesheet_link_tag+ +* +image_tag+ You can use these tags in layouts or other views, although the tags other than +image_tag+ are most commonly used in the +<head>+ section of a layout. @@ -622,16 +622,16 @@ To include +public/stylesheets/main.css+ and +public/photos/columns.css+: <%= stylesheet_link_tag "main", "/photos/columns" %> </erb> -To include +http://example.com/main.cs+: +To include +http://example.com/main.css+: <erb> -<%= stylesheet_link_tag "http://example.com/main.cs" %> +<%= 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, +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" %> +<%= stylesheet_link_tag "main_print", :media => "print" %> </erb> The +all+ option links every CSS file in +public/stylesheets+: @@ -958,13 +958,13 @@ On pages generated by +NewsController+, you want to hide the top menu and add a <% content_for :content do %> <div id="right_menu">Right menu items here</div> <%= yield(:news_content) or yield %> - <% end -%> +<% end -%> <%= render :file => 'layouts/application' %> </erb> That's it. The News views will use the new layout, hiding the top menu and adding a new right menu inside the "content" div. -There are several ways of getting similar results with different sub-templating schemes using this technique. Note that there is no limit in nesting levels. One can use the +ActionView::render+ method via +render :file => 'layouts/news'+ to base a new layout on the News layout. If one is sure she will not subtemplate the +News+ layout, she can ommit the +yield(:news_content) or + part. +There are several ways of getting similar results with different sub-templating schemes using this technique. Note that there is no limit in nesting levels. One can use the +ActionView::render+ method via +render :file => 'layouts/news'+ to base a new layout on the News layout. If one is sure she will not subtemplate the +News+ layout, she can omit the +yield(:news_content) or + part. h3. Changelog diff --git a/railties/guides/source/migrations.textile b/railties/guides/source/migrations.textile index 5ed94c30b7..5f49ac41f5 100644 --- a/railties/guides/source/migrations.textile +++ b/railties/guides/source/migrations.textile @@ -1,10 +1,10 @@ h2. Migrations -Migrations are a convenient way for you to alter your database in a structured and organised manner. You could edit fragments of SQL by hand but you would then be responsible for telling other developers that they need to go and run it. You'd also have to keep track of which changes need to be run against the production machines next time you deploy. +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 it. You'd also have to keep track of which changes need to be run against the production machines next time you deploy. Active Record tracks which migrations have already been run so all you have to do is update your source and run +rake db:migrate+. Active Record will work out which migrations should be run. It will also update your +db/schema.rb+ file to match the structure of your database. -Migrations also allow you to describe these transformations using Ruby. The great thing about this is that (like most of Active Record's functionality) it is database independent: you don't need to worry about the precise syntax of +CREATE TABLE+ any more that you worry about variations on +SELECT *+ (you can drop down to raw SQL for database specific features). For example you could use SQLite3 in development, but MySQL in production. +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: @@ -78,7 +78,7 @@ Active Record provides methods that perform common data definition tasks in a da 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). -On databases that support transactions with statements that change the schema (such as PostgreSQL), migrations are wrapped in a transaction. If the database does not support this (for example MySQL and SQLite) 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 unpick the changes that were made by hand. h4. What's in a Name @@ -508,7 +508,7 @@ The migration has its own minimal copy of the +Product+ model and no longer care h4. Dealing with Changing Models -For performance reasons information about the columns a model has is cached. For example if you add a column to a table and then try and use the corresponding model to insert a new row it may try and use the old column information. You can force Active Record to re-read the column information with the +reset_column_information+ method, for example +For performance reasons information about the columns a model has is cached. For example if you add a column to a table and then try and use the corresponding model to insert a new row it may try to use the old column information. You can force Active Record to re-read the column information with the +reset_column_information+ method, for example <ruby> class AddPartNumberToProducts < ActiveRecord::Migration @@ -538,7 +538,7 @@ There is no need (and it is error prone) to deploy a new instance of an app by r For example, this is how the test database is created: the current development database is dumped (either to +db/schema.rb+ or +db/development.sql+) and then loaded into the test database. -Schema files are also useful if you want a quick look at what attributes an Active Record object has. This information is not in the model's code and is frequently spread across several migrations but is all summed up in the schema file. The "annotate_models":http://agilewebdevelopment.com/plugins/annotate_models plugin, which automatically adds (and updates) comments at the top of each model summarising the schema, may also be of interest. +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. h4. Types of Schema Dumps @@ -582,7 +582,7 @@ The Active Record way claims that intelligence belongs in your models, not in th Validations such as +validates_uniqueness_of+ are one way in which models can enforce data integrity. The +:dependent+ option on associations allows models to automatically destroy child objects when the parent is destroyed. Like anything which operates at the application level these cannot guarantee referential integrity and so some people augment them with foreign key constraints. -Although Active Record does not provide any tools for working directly with such features, the +execute+ method can be used to execute arbitrary SQL. There are also a number of plugins such as "redhillonrails":http://agilewebdevelopment.com/plugins/search?search=redhillonrails which add foreign key support to Active Record (including support for dumping foreign keys in +db/schema.rb+). +Although Active Record does not provide any tools for working directly with such features, the +execute+ method can be used to execute arbitrary SQL. There are also a number of plugins such as "foreign_key_migrations":http://github.com/harukizaemon/foreign_key_migrations/tree/master which add foreign key support to Active Record (including support for dumping foreign keys in +db/schema.rb+). h3. Changelog diff --git a/railties/guides/source/performance_testing.textile b/railties/guides/source/performance_testing.textile index 320a5b8472..f0dc9acbb8 100644 --- a/railties/guides/source/performance_testing.textile +++ b/railties/guides/source/performance_testing.textile @@ -293,7 +293,7 @@ WARNING: Performance test configurability is not yet enabled in Rails. But it wi h4. Performance Test Environment -Performance tests are run in the +development+ environment. But running performance tests will set the following configuration parameters: +Performance tests are run in the +test+ environment. But running performance tests will set the following configuration parameters: <shell> ActionController::Base.perform_caching = true diff --git a/railties/guides/source/plugins.textile b/railties/guides/source/plugins.textile index 55ecdcd3d1..a5d39c3d09 100644 --- a/railties/guides/source/plugins.textile +++ b/railties/guides/source/plugins.textile @@ -601,7 +601,7 @@ This is just a simple test to make sure the class is being loaded correctly. Af end </ruby> -Adding directories to the load path makes them appear just like files in the the main app directory - except that they are only loaded once, so you have to restart the web server to see the changes in the browser. Removing directories from the 'load_once_paths' allow those changes to picked up as soon as you save the file - without having to restart the web server. This is particularly useful as you develop the plugin. +Adding directories to the load path makes them appear just like files in the main app directory - except that they are only loaded once, so you have to restart the web server to see the changes in the browser. Removing directories from the 'load_once_paths' allow those changes to picked up as soon as you save the file - without having to restart the web server. This is particularly useful as you develop the plugin. * *vendor/plugins/yaffle/lib/app/models/woodpecker.rb:* diff --git a/railties/guides/source/rails_application_templates.textile b/railties/guides/source/rails_application_templates.textile index 49cd5bf5f5..fc178fa44b 100644 --- a/railties/guides/source/rails_application_templates.textile +++ b/railties/guides/source/rails_application_templates.textile @@ -1,18 +1,238 @@ h2. Rails Application Templates -This guide covers the Rails application templates, By referring to this guide, you will be able to: +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. + +By referring to this guide, you will be able to: -* Use existing templates to generate a customized Rails application -* Write your own reusable Rails application templates +* Use templates to generate/customize Rails applications +* Write your own reusable application templates using the Rails template API endprologue. -h3. Introduction +h3. Usage -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. +To apply a template, you need to provide the Rails generator with the location of the template you wish to apply, using -m option : + +<shell> +$ rails blog -m ~/template.rb +</shell> + +It's also possible to apply a template using a URL : + +<shell> +$ rails blog -m http://gist.github.com/31208.txt +</shell> + +Alternatively, you can use the rake task +rails:template+ to apply a template to an existing Rails application : + +<shell> +$ rake rails:template LOCATION=~/template.rb +</shell> + +h3. Template API + +Rails templates API is very self explanatory and easy to understand. Here's an example of a typical Rails template : + +<ruby> +# template.rb +run "rm public/index.html" +generate(:scaffold, "person name:string") +route "map.root :controller => 'people'" +rake("db:migrate") + +git :init +git :add => "." +git :commit => "-a -m 'Initial commit'" +</ruby> + +The following sections outlines the primary methods provided by the API : + +h4. gem(name, options = {}) + +Adds a +config.gem+ entry for the supplied gem to the generated application’s +config/environment.rb+. + +For example, if your application depends on the gems +bj+ and +hpricot+ : + +<ruby> +gem "bj" +gem "hpricot", :version => '0.6', :source => "http://code.whytheluckystiff.net" +</ruby> + +Please note that this will NOT install the gems for you. So you may want to run the +rake gems:install+ task too : + +<ruby> +rake "gems:install" +</ruby> + +And let Rails take care of installing the required gems if they’re not already installed. + +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 'wtfsvn', :svn => 'svn://crap.com/wtf/trunk' +</ruby> + +h4. vendor/lib/file/initializer(filename, data = nil, &block) + +Adds an initializer to the generated application’s +config/initializers+ directory. + +Lets say you like using +Object#not_nil?+ and +Object#not_blank?+ : + +<ruby> +initializer 'bloatlol.rb', <<-CODE +class Object + def not_nil? + !nil? + end + + def not_blank? + !blank? + end +end +CODE +</ruby> + +Similarly +lib()+ creates a file in the +lib/+ directory and +vendor()+ creates a file in the +vendor/+ directory. + +There is even +file()+, which accepts a relative path from +RAILS_ROOT+ and creates all the directories/file needed : + +<ruby> +file 'app/components/foo.rb', <<-CODE +class Foo +end +CODE +</ruby> + +That’ll create +app/components+ directory and put +foo.rb+ in there. + +h4. rakefile(filename, data = nil, &block) + +Creates a new rake file under +lib/tasks+ with the supplied tasks : + +<ruby> +rakefile("bootstrap.rake") do + <<-TASK + namespace :boot do + task :strap do + puts "i like boots!" + end + end + TASK +end +</ruby> + +The above creates +lib/tasks/bootstrap.rake+ with a +boot:strap+ rake task. + +h4. generate(what, args) + +Runs the supplied rails generator with given arguments. For example, I love to scaffold some whenever I’m playing with Rails : + +<ruby> +generate(:scaffold, "person", "name:string", "address:text", "age:number") +</ruby> + +h4. run(command) + +Executes an arbitrary command. Just like the backticks. Let's say you want to remove the +public/index.html+ file : + +<ruby> +run "rm public/index.html" +</ruby> + +h4. rake(command, options = {}) + +Runs the supplied rake tasks in the Rails application. Let's say you want to migrate the database : + +<ruby> +rake "db:migrate" +</ruby> + +You can also run rake tasks with a different Rails environment : + +<ruby> +rake "db:migrate", :env => 'production' +</ruby> + +Or even use sudo : + +<ruby> +rake "gems:install", :sudo => true +</ruby> + +h4. route(routing_code) + +This adds a routing entry to the +config/routes.rb+ file. In above steps, we generated a person scaffold and also removed +public/index.html+. Now to make +PeopleController#index+ as the default page for the application : + +<ruby> +route "map.root :controller => :person" +</ruby> + +h4. inside(dir) + +I have my edge rails lying at +~/commit-rails/rails+. So every time i have to manually symlink edge from my new app. But now : + +<ruby> +inside('vendor') do + run "ln -s ~/commit-rails/rails rails" +end +</ruby> + +So +inside()+ runs the command from the given directory. + +h4. ask(question) + ++ask()+ gives you a chance to get some feedback from the user and use it in your templates. Lets say you want your user to name the new shiny library you’re adding : + +<ruby> +lib_name = ask("What do you want to call the shiny library ?") +lib_name << ".rb" unless lib_name.index(".rb") + +lib lib_name, <<-CODE +class Shiny +end +CODE +</ruby> + +h4. yes?(question) or no?(question) + +These methods let you ask questions from templates and decide the flow based on the user’s answer. Lets say you want to freeze rails only if the user want to : + +<ruby> +rake("rails:freeze:gems") if yes?("Freeze rails gems ?") +no?(question) acts just the opposite. +</ruby> + +h4. git(:must => "-a love") + +Rails templates let you run any git command : + +<ruby> +git :init +git :add => "." +git :commit => "-a -m 'Initial commit'" +</ruby> h3. Changelog "Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/78 -* April 17, 2009: Initial version by "Pratik":credits.html#lifo +* April 29, 2009: Initial version by "Pratik":credits.html#lifo diff --git a/railties/guides/source/rails_on_rack.textile b/railties/guides/source/rails_on_rack.textile index 1164ed821d..545aaa18e0 100644 --- a/railties/guides/source/rails_on_rack.textile +++ b/railties/guides/source/rails_on_rack.textile @@ -48,7 +48,7 @@ app = Rack::Builder.new { }.to_app </ruby> -Middlewares used in the code above are primarily useful only in the development envrionment. The following table explains their usage: +Middlewares used in the code above are primarily useful only in the development environment. The following table explains their usage: |_.Middleware|_.Purpose| |+Rails::Rack::LogTailer+|Appends log file output to console| @@ -229,7 +229,7 @@ h3. Rails Metal Applications Rails Metal applications are minimal Rack applications specially designed for integrating with a typical Rails application. As Rails Metal Applications skip all of the Action Controller stack, serving a request has no overhead from the Rails framework itself. This is especially useful for infrequent cases where the performance of the full stack Rails framework is an issue. -Ryan Bates' "railscast on Rails Metal":http://railscasts.com/episodes/150-rails-metal provides a nice walkthrough generating and using Rails Metal. +Ryan Bates' "Railscast on Rails Metal":http://railscasts.com/episodes/150-rails-metal provides a nice walkthrough generating and using Rails Metal. h4. Generating a Metal Application diff --git a/railties/guides/source/routing.textile b/railties/guides/source/routing.textile index e9adb4b308..355f385d49 100644 --- a/railties/guides/source/routing.textile +++ b/railties/guides/source/routing.textile @@ -236,7 +236,7 @@ will recognize incoming URLs containing +photo+ but route the requests to the Im |GET |/photos/new |Images |new |return an HTML form for creating a new image| |POST |/photos |Images |create |create a new image| |GET |/photos/1 |Images |show |display a specific image| -|GET |/photos/1/edit |Images |edit |return an HTML form for editing a image| +|GET |/photos/1/edit |Images |edit |return an HTML form for editing an image| |PUT |/photos/1 |Images |update |update a specific image| |DELETE |/photos/1 |Images |destroy |delete a specific image| @@ -416,7 +416,7 @@ map.resources :magazines do |magazine| end </ruby> -TIP: Further below you'll learn about a convenient shortcut for this construct:<br/>+map.resources :magazines, :has_many => :ads+. +TIP: Further below you'll learn about a convenient shortcut for this construct:<br/>+map.resources :magazines, :has_many => :ads+ In addition to the routes for magazines, this declaration will also create routes for ads, each of which requires the specification of a magazine in the URL: @@ -614,7 +614,7 @@ To add a new route (one that creates a new resource), use the +:new+ option: map.resources :photos, :new => { :upload => :post } </ruby> -This will enable Rails to recognize URLs such as +/photos/upload+ using the POST HTTP verb, and route them to the upload action of the Photos controller. It will also create the +upload_photos_path+ and +upload_photos_url+ route helpers. +This will enable Rails to recognize URLs such as +/photos/new/upload+ using the POST HTTP verb, and route them to the upload action of the Photos controller. It will also create the +upload_new_photos_path+ and +upload_new_photos_url+ route helpers. TIP: If you want to redefine the verbs accepted by one of the standard actions, you can do so by explicitly mapping that action. For example:<br/>+map.resources :photos, :new => { :new => :any }+<br/>This will allow the new action to be invoked by any request to +photos/new+, no matter what HTTP verb you use. @@ -695,7 +695,7 @@ Regular routes need not use the +connect+ method. You can use any other name her map.logout '/logout', :controller => 'sessions', :action => 'destroy' </ruby> -This will do two things. First, requests to +/logout+ will be sent to the +destroy+ method of the +Sessions+ controller. Second, Rails will maintain the +logout_path+ and +logout_url+ helpers for use within your code. +This will do two things. First, requests to +/logout+ will be sent to the +destroy+ action of the +Sessions+ controller. Second, Rails will maintain the +logout_path+ and +logout_url+ helpers for use within your code. h4. Route Requirements diff --git a/railties/guides/source/security.textile b/railties/guides/source/security.textile index 1b64cc1be7..c26bea5519 100644 --- a/railties/guides/source/security.textile +++ b/railties/guides/source/security.textile @@ -392,7 +392,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. 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 @@ -497,7 +497,7 @@ Depending on your web application, there may be more ways to hijack the user's a h4. CAPTCHAs --- _A CAPTCHA is a challenge-response test to determine that the response is not generated by a computer. It is often used to protect comment forms from automatic spam bots by asking the user to type the letters of a distorted image. The idea of a negative CAPTCHA is not to ask a user to proof that he is human, but reveal that a robot is a robot._ +-- _A CAPTCHA is a challenge-response test to determine that the response is not generated by a computer. It is often used to protect comment forms from automatic spam bots by asking the user to type the letters of a distorted image. The idea of a negative CAPTCHA is not for a user to prove that he is human, but reveal that a robot is a robot._ But not only spam robots (bots) are a problem, but also automatic login bots. A popular CAPTCHA API is "reCAPTCHA":http://recaptcha.net/ which displays two distorted images of words from old books. It also adds an angled line, rather than a distorted background and high levels of warping on the text as earlier CAPTCHAs did, because the latter were broken. As a bonus, using reCAPTCHA helps to digitize old books. "ReCAPTCHA":http://ambethia.com/recaptcha/ is also a Rails plug-in with the same name as the API. @@ -967,7 +967,7 @@ Transfer-Encoding: chunked Content-Type: text/html </plain> -Under certain circumstances this would present the malicious HTML to the victim. However, this seems to work with Keep-Alive connections, only (and many browsers are using one-time connections). But you can't rely on this. _(highlight)In any case this is a serious bug, and you should update your Rails to version 2.0.5 or 2.1.2 to eliminate Header Injection (and thus response splitting) risks._ +Under certain circumstances this would present the malicious HTML to the victim. However, this only seems to work with Keep-Alive connections (and many browsers are using one-time connections). But you can't rely on this. _(highlight)In any case this is a serious bug, and you should update your Rails to version 2.0.5 or 2.1.2 to eliminate Header Injection (and thus response splitting) risks._ h3. Additional Resources diff --git a/railties/guides/source/testing.textile b/railties/guides/source/testing.textile index 12fc836edf..8318146ed3 100644 --- a/railties/guides/source/testing.textile +++ b/railties/guides/source/testing.textile @@ -211,7 +211,7 @@ $ rake db:migrate $ rake db:test:load </shell> -Above +rake db:migrate+ runs any pending migrations on the _developemnt_ environment and updates +db/schema.rb+. +rake db:test:load+ recreates the test database from the current db/schema.rb. On subsequent attempts it is a good to first run +db:test:prepare+ as it first checks for pending migrations and warns you appropriately. +Above +rake db:migrate+ runs any pending migrations on the _development_ environment and updates +db/schema.rb+. +rake db:test:load+ recreates the test database from the current db/schema.rb. On subsequent attempts it is a good to first run +db:test:prepare+ as it first checks for pending migrations and warns you appropriately. NOTE: +db:test:prepare+ will fail with an error if db/schema.rb doesn't exists. @@ -372,7 +372,7 @@ NameError: undefined local variable or method `some_undefined_variable' for #<Po Notice the 'E' in the output. It denotes a test with error. -NOTE: The execution of each test method stops as soon as any error or a assertion failure is encountered, and the test suite continues with the next method. All test methods are executed in alphabetical order. +NOTE: The execution of each test method stops as soon as any error or an assertion failure is encountered, and the test suite continues with the next method. All test methods are executed in alphabetical order. h4. What to Include in Your Unit Tests @@ -413,6 +413,8 @@ h4. Rails Specific Assertions Rails adds some custom assertions of its own to the +test/unit+ framework: +NOTE: +assert_valid(record)+ has been deprecated. Please use +assert(record.valid?)+ instead. + |_.Assertion |_.Purpose| |+assert_valid(record)+ |Ensures that the passed record is valid by Active Record standards and returns any error messages if it is not.| |+assert_difference(expressions, difference = 1, message = nil) {...}+ |Test numeric difference between the return value of an expression as a result of what is evaluated in the yielded block.| @@ -437,7 +439,7 @@ You should test for things such as: * was the user redirected to the right page? * was the user successfully authenticated? * was the correct object stored in the response template? -* was the appropriate message displayed to the user in the view +* was the appropriate message displayed to the user in the view? Now that we have used Rails scaffold generator for our +Post+ resource, it has already created the controller code and functional tests. You can take look at the file +posts_controller_test.rb+ in the +test/functional+ directory. @@ -590,7 +592,7 @@ There are more assertions that are primarily used in testing views: |_.Assertion |_.Purpose| |+assert_select_email+ |Allows you to make assertions on the body of an e-mail. | -|+assert_select_rjs+ |Allows you to make assertions on RJS response. +assert_select_rjs+ has variants which allow you to narrow down on the updated element or even a particular operation on an element.| +|+assert_select_rjs+ |Allows you to make assertions on an RJS response. +assert_select_rjs+ has variants which allow you to narrow down on the updated element or even a particular operation on an element.| |+assert_select_encoded+ |Allows you to make assertions on encoded HTML. It does this by un-encoding the contents of each element and then calling the block with all the un-encoded elements.| |+css_select(selector)+ or +css_select(element, selector)+ |Returns an array of all the elements selected by the _selector_. In the second variant it first matches the base _element_ and tries to match the _selector_ expression on any of its children. If there are no matches both variants return an empty array.| @@ -699,7 +701,7 @@ class UserFlowsTest < ActionController::IntegrationTest # User avs can browse site avs.browses_site - # User guest can browse site aswell + # User guest can browse site as well guest.browses_site # Continue with other assertions @@ -730,7 +732,7 @@ end h3. Rake Tasks for Running your Tests -You don't need to set up and run your tests by hand on a test-by-test basis. Rails comes with a number of rake tasks to help in testing. The table below lists all rake tasks that come along in the default Rakefile when you initiate a Rail project. +You don't need to set up and run your tests by hand on a test-by-test basis. Rails comes with a number of rake tasks to help in testing. The table below lists all rake tasks that come along in the default Rakefile when you initiate a Rails project. |_.Tasks |_.Description| |+rake test+ |Runs all unit, functional and integration tests. You can also simply run +rake+ as the _test_ target is the default.| @@ -836,7 +838,7 @@ end h3. Testing Routes -Like everything else in you Rails application, it's recommended to test you routes. An example test for a route in the default +show+ action of +Posts+ controller above should look like: +Like everything else in your Rails application, it is recommended that you test your routes. An example test for a route in the default +show+ action of +Posts+ controller above should look like: <ruby> def test_should_route_to_post @@ -860,7 +862,7 @@ The goals of testing your +ActionMailer+ classes are to ensure that: h5. From All Sides -There are two aspects of testing your mailer, the unit tests and the functional tests. In the unit tests, you run the mailer in isolation with tightly controlled inputs and compare the output to a knownvalue (a fixture -- yay! more fixtures!). In the functional tests you don't so much test the minute details produced by the mailer Instead we test that our controllers and models are using the mailer in the right way. You test to prove that the right email was sent at the right time. +There are two aspects of testing your mailer, the unit tests and the functional tests. In the unit tests, you run the mailer in isolation with tightly controlled inputs and compare the output to a known value (a fixture -- yay! more fixtures!). In the functional tests you don't so much test the minute details produced by the mailer Instead we test that our controllers and models are using the mailer in the right way. You test to prove that the right email was sent at the right time. h4. Unit Testing |