aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/address.rb4
-rw-r--r--actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/header.rb2
-rw-r--r--actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/interface.rb2
-rw-r--r--actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/mail.rb4
-rw-r--r--actionpack/CHANGELOG6
-rw-r--r--actionpack/README18
-rw-r--r--actionpack/lib/action_controller/assertions/selector_assertions.rb6
-rwxr-xr-xactionpack/lib/action_controller/base.rb2
-rw-r--r--actionpack/lib/action_controller/caching/fragments.rb2
-rw-r--r--actionpack/lib/action_controller/cookies.rb10
-rw-r--r--actionpack/lib/action_controller/integration.rb2
-rw-r--r--actionpack/lib/action_controller/rack_process.rb10
-rwxr-xr-xactionpack/lib/action_controller/request.rb6
-rw-r--r--actionpack/lib/action_controller/request_forgery_protection.rb2
-rw-r--r--actionpack/lib/action_controller/rescue.rb28
-rw-r--r--actionpack/lib/action_controller/resources.rb4
-rw-r--r--actionpack/lib/action_controller/streaming.rb23
-rw-r--r--actionpack/lib/action_controller/test_case.rb23
-rw-r--r--actionpack/lib/action_controller/test_process.rb4
-rw-r--r--actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb4
-rw-r--r--actionpack/lib/action_view/base.rb9
-rw-r--r--actionpack/lib/action_view/helpers/active_record_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helper.rb25
-rw-r--r--actionpack/lib/action_view/helpers/capture_helper.rb17
-rwxr-xr-xactionpack/lib/action_view/helpers/date_helper.rb4
-rw-r--r--actionpack/lib/action_view/helpers/debug_helper.rb33
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb35
-rw-r--r--actionpack/lib/action_view/helpers/javascript_helper.rb21
-rw-r--r--actionpack/lib/action_view/helpers/prototype_helper.rb7
-rw-r--r--actionpack/lib/action_view/helpers/sanitize_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/scriptaculous_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/tag_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/url_helper.rb2
-rw-r--r--actionpack/lib/action_view/paths.rb16
-rw-r--r--actionpack/lib/action_view/renderable.rb53
-rw-r--r--actionpack/test/abstract_unit.rb2
-rw-r--r--actionpack/test/controller/base_test.rb20
-rw-r--r--actionpack/test/controller/caching_test.rb62
-rwxr-xr-xactionpack/test/controller/cgi_test.rb50
-rw-r--r--actionpack/test/controller/cookie_test.rb2
-rw-r--r--actionpack/test/controller/rack_test.rb81
-rw-r--r--actionpack/test/controller/rescue_test.rb9
-rw-r--r--actionpack/test/fixtures/functional_caching/formatted_fragment_cached.html.erb3
-rw-r--r--actionpack/test/fixtures/functional_caching/formatted_fragment_cached.js.rjs6
-rw-r--r--actionpack/test/fixtures/functional_caching/formatted_fragment_cached.xml.builder5
-rw-r--r--actionpack/test/template/compiled_templates_test.rb41
-rw-r--r--actionpack/test/template/javascript_helper_test.rb6
-rw-r--r--actionpack/test/template/prototype_helper_test.rb6
-rw-r--r--activerecord/CHANGELOG2
-rw-r--r--activerecord/lib/active_record/association_preload.rb2
-rwxr-xr-xactiverecord/lib/active_record/associations.rb7
-rwxr-xr-xactiverecord/lib/active_record/base.rb13
-rwxr-xr-xactiverecord/lib/active_record/callbacks.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb2
-rw-r--r--activerecord/lib/active_record/dirty.rb12
-rw-r--r--activerecord/lib/active_record/migration.rb16
-rwxr-xr-xactiverecord/lib/active_record/validations.rb44
-rw-r--r--activeresource/README2
-rw-r--r--activeresource/lib/active_resource/base.rb22
-rw-r--r--activeresource/lib/active_resource/custom_methods.rb4
-rw-r--r--activeresource/lib/active_resource/http_mock.rb2
-rw-r--r--activesupport/lib/active_support/cache/mem_cache_store.rb30
-rw-r--r--activesupport/lib/active_support/core_ext/bigdecimal/conversions.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/hash.rb3
-rw-r--r--activesupport/lib/active_support/core_ext/hash/deep_merge.rb23
-rw-r--r--activesupport/lib/active_support/core_ext/hash/except.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/hash/reverse_merge.rb17
-rw-r--r--activesupport/lib/active_support/core_ext/hash/slice.rb5
-rw-r--r--activesupport/lib/active_support/core_ext/object.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/object/metaclass.rb8
-rw-r--r--activesupport/lib/active_support/core_ext/string/inflections.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/time/calculations.rb2
-rw-r--r--activesupport/lib/active_support/json.rb2
-rw-r--r--activesupport/lib/active_support/json/encoders/date.rb11
-rw-r--r--activesupport/lib/active_support/json/encoders/date_time.rb11
-rw-r--r--activesupport/lib/active_support/json/encoders/time.rb13
-rw-r--r--activesupport/lib/active_support/option_merger.rb10
-rw-r--r--activesupport/lib/active_support/time_with_zone.rb14
-rw-r--r--activesupport/lib/active_support/vendor/builder-2.1.2/builder/xmlevents.rb2
-rw-r--r--activesupport/lib/active_support/vendor/memcache-client-1.5.0/memcache.rb4
-rw-r--r--activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/data_timezone.rb2
-rw-r--r--activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/data_timezone_info.rb2
-rw-r--r--activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/linked_timezone.rb2
-rw-r--r--activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/timezone.rb2
-rw-r--r--activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/timezone_transition_info.rb2
-rw-r--r--activesupport/lib/active_support/vendor/xml-simple-1.0.11/xmlsimple.rb6
-rw-r--r--activesupport/test/core_ext/hash_ext_test.rb31
-rw-r--r--activesupport/test/core_ext/object_and_class_ext_test.rb8
-rw-r--r--activesupport/test/option_merger_test.rb27
-rw-r--r--railties/config.ru17
-rw-r--r--railties/doc/guides/actionview/helpers.markdown91
-rw-r--r--railties/doc/guides/actionview/partials.markdown90
-rw-r--r--railties/doc/guides/activerecord/basics.markdown56
-rw-r--r--railties/doc/guides/creating_plugins/basics.markdown861
-rw-r--r--railties/environments/production.rb1
-rw-r--r--railties/lib/commands/process/spawner.rb4
-rw-r--r--railties/lib/initializer.rb11
-rw-r--r--railties/lib/performance_test_help.rb1
-rw-r--r--railties/lib/rails_generator/commands.rb17
-rw-r--r--railties/lib/rails_generator/generators/applications/app/app_generator.rb1
-rw-r--r--railties/lib/rails_generator/generators/components/scaffold/USAGE14
-rw-r--r--railties/lib/rails_generator/scripts.rb2
-rw-r--r--railties/lib/rails_generator/scripts/destroy.rb13
-rw-r--r--railties/lib/rails_generator/secret_key_generator.rb2
-rw-r--r--railties/test/generators/generator_test_helper.rb2
106 files changed, 1950 insertions, 296 deletions
diff --git a/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/address.rb b/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/address.rb
index fa8e5bcd8c..982ad5b661 100644
--- a/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/address.rb
+++ b/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/address.rb
@@ -38,7 +38,7 @@ module TMail
# = Class Address
#
# Provides a complete handling library for email addresses. Can parse a string of an
- # address directly or take in preformatted addresses themseleves. Allows you to add
+ # address directly or take in preformatted addresses themselves. Allows you to add
# and remove phrases from the front of the address and provides a compare function for
# email addresses.
#
@@ -143,7 +143,7 @@ module TMail
# This is to catch an unquoted "@" symbol in the local part of the
# address. Handles addresses like <"@"@me.com> and makes sure they
- # stay like <"@"@me.com> (previously were becomming <@@me.com>)
+ # stay like <"@"@me.com> (previously were becoming <@@me.com>)
if local && (local.join == '@' || local.join =~ /\A[^"].*?@.*?[^"]\Z/)
@local = "\"#{local.join}\""
else
diff --git a/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/header.rb b/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/header.rb
index 9153dcd7c6..dbdefcf979 100644
--- a/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/header.rb
+++ b/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/header.rb
@@ -59,7 +59,7 @@ module TMail
#
# This is because a mailbox doesn't have the : after the From that designates the
# beginning of the envelope sender (which can be different to the from address of
- # the emial)
+ # the email)
#
# Other fields can be passed as normal, "Reply-To", "Received" etc.
#
diff --git a/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/interface.rb b/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/interface.rb
index a6d428d7d6..2fc2dbdfc7 100644
--- a/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/interface.rb
+++ b/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/interface.rb
@@ -42,7 +42,7 @@ module TMail
# Allows you to query the mail object with a string to get the contents
# of the field you want.
#
- # Returns a string of the exact contnts of the field
+ # Returns a string of the exact contents of the field
#
# mail.from = "mikel <mikel@lindsaar.net>"
# mail.header_string("From") #=> "mikel <mikel@lindsaar.net>"
diff --git a/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/mail.rb b/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/mail.rb
index 5a319907ae..c3a8803dc4 100644
--- a/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/mail.rb
+++ b/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/mail.rb
@@ -255,7 +255,7 @@ module TMail
alias fetch []
# Allows you to set or delete TMail header objects at will.
- # Eamples:
+ # Examples:
# @mail = TMail::Mail.new
# @mail['to'].to_s # => 'mikel@test.com.au'
# @mail['to'] = 'mikel@elsewhere.org'
@@ -265,7 +265,7 @@ module TMail
# @mail['to'].to_s # => nil
# @mail.encoded # => "\r\n"
#
- # Note: setting mail[] = nil actualy deletes the header field in question from the object,
+ # Note: setting mail[] = nil actually deletes the header field in question from the object,
# it does not just set the value of the hash to nil
def []=( key, val )
dkey = key.downcase
diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG
index 52d00a417c..da9fdbfd9d 100644
--- a/actionpack/CHANGELOG
+++ b/actionpack/CHANGELOG
@@ -1,5 +1,11 @@
*Edge*
+* All 2xx requests are considered successful [Josh Peek]
+
+* Fixed that AssetTagHelper#compute_public_path shouldn't cache the asset_host along with the source or per-request proc's won't run [DHH]
+
+* Removed config.action_view.cache_template_loading, use config.cache_classes instead [Josh Peek]
+
* Get buffer for fragment cache from template's @output_buffer [Josh Peek]
* Set config.action_view.warn_cache_misses = true to receive a warning if you perform an action that results in an expensive disk operation that could be cached [Josh Peek]
diff --git a/actionpack/README b/actionpack/README
index 2746c3cc43..6090089bb9 100644
--- a/actionpack/README
+++ b/actionpack/README
@@ -31,7 +31,7 @@ http://www.rubyonrails.org.
A short rundown of the major features:
* Actions grouped in controller as methods instead of separate command objects
- and can therefore share helper methods.
+ and can therefore share helper methods
BlogController < ActionController::Base
def show
@@ -168,7 +168,7 @@ A short rundown of the major features:
{Learn more}[link:classes/ActionController/Base.html]
-* Javascript and Ajax integration.
+* Javascript and Ajax integration
link_to_function "Greeting", "alert('Hello world!')"
link_to_remote "Delete this post", :update => "posts",
@@ -177,7 +177,7 @@ A short rundown of the major features:
{Learn more}[link:classes/ActionView/Helpers/JavaScriptHelper.html]
-* Pagination for navigating lists of results.
+* Pagination for navigating lists of results
# controller
def list
@@ -192,15 +192,9 @@ A short rundown of the major features:
{Learn more}[link:classes/ActionController/Pagination.html]
-* Easy testing of both controller and template result through TestRequest/Response
-
- class LoginControllerTest < Test::Unit::TestCase
- def setup
- @controller = LoginController.new
- @request = ActionController::TestRequest.new
- @response = ActionController::TestResponse.new
- end
+* Easy testing of both controller and rendered template through ActionController::TestCase
+ class LoginControllerTest < ActionController::TestCase
def test_failing_authenticate
process :authenticate, :user_name => "nop", :password => ""
assert flash.has_key?(:alert)
@@ -208,7 +202,7 @@ A short rundown of the major features:
end
end
- {Learn more}[link:classes/ActionController/TestRequest.html]
+ {Learn more}[link:classes/ActionController/TestCase.html]
* Automated benchmarking and integrated logging
diff --git a/actionpack/lib/action_controller/assertions/selector_assertions.rb b/actionpack/lib/action_controller/assertions/selector_assertions.rb
index d3594e711c..70b0ed53e7 100644
--- a/actionpack/lib/action_controller/assertions/selector_assertions.rb
+++ b/actionpack/lib/action_controller/assertions/selector_assertions.rb
@@ -21,10 +21,8 @@ module ActionController
# from the response HTML or elements selected by the enclosing assertion.
#
# In addition to HTML responses, you can make the following assertions:
- # * +assert_select_rjs+ - Assertions on HTML content of RJS update and
- # insertion operations.
- # * +assert_select_encoded+ - Assertions on HTML encoded inside XML,
- # for example for dealing with feed item descriptions.
+ # * +assert_select_rjs+ - Assertions on HTML content of RJS update and insertion operations.
+ # * +assert_select_encoded+ - Assertions on HTML encoded inside XML, for example for dealing with feed item descriptions.
# * +assert_select_email+ - Assertions on the HTML body of an e-mail.
#
# Also see HTML::Selector to learn how to use selectors.
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index df94f78f18..c56812c2d9 100755
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -1155,7 +1155,7 @@ module ActionController #:nodoc:
def log_processing
if logger && logger.info?
- logger.info "\n\nProcessing #{controller_class_name}\##{action_name} (for #{request_origin}) [#{request.method.to_s.upcase}]"
+ logger.info "\n\nProcessing #{self.class.name}\##{action_name} (for #{request_origin}) [#{request.method.to_s.upcase}]"
logger.info " Session ID: #{@_session.session_id}" if @_session and @_session.respond_to?(:session_id)
logger.info " Parameters: #{respond_to?(:filter_parameters) ? filter_parameters(params).inspect : params.inspect}"
end
diff --git a/actionpack/lib/action_controller/caching/fragments.rb b/actionpack/lib/action_controller/caching/fragments.rb
index b1f25fdf5c..e9b434dd25 100644
--- a/actionpack/lib/action_controller/caching/fragments.rb
+++ b/actionpack/lib/action_controller/caching/fragments.rb
@@ -2,7 +2,7 @@ module ActionController #:nodoc:
module Caching
# Fragment caching is used for caching various blocks within templates without caching the entire action as a whole. This is useful when
# certain elements of an action change frequently or depend on complicated state while other parts rarely change or can be shared amongst multiple
- # parties. The caching is doing using the cache helper available in the Action View. A template with caching might look something like:
+ # parties. The caching is done using the cache helper available in the Action View. A template with caching might look something like:
#
# <b>Hello <%= @name %></b>
# <% cache do %>
diff --git a/actionpack/lib/action_controller/cookies.rb b/actionpack/lib/action_controller/cookies.rb
index a4cddbcea2..0428f2a23d 100644
--- a/actionpack/lib/action_controller/cookies.rb
+++ b/actionpack/lib/action_controller/cookies.rb
@@ -22,6 +22,16 @@ module ActionController #:nodoc:
#
# cookies.delete :user_name
#
+ # Please note that if you specify a :domain when setting a cookie, you must also specify the domain when deleting the cookie:
+ #
+ # cookies[:key] = {
+ # :value => 'a yummy cookie',
+ # :expires => 1.year.from_now,
+ # :domain => 'domain.com'
+ # }
+ #
+ # cookies.delete(:key, :domain => 'domain.com')
+ #
# The option symbols for setting cookies are:
#
# * <tt>:value</tt> - The cookie's value or list of values (as an array).
diff --git a/actionpack/lib/action_controller/integration.rb b/actionpack/lib/action_controller/integration.rb
index 18c2df8b37..2a732448f2 100644
--- a/actionpack/lib/action_controller/integration.rb
+++ b/actionpack/lib/action_controller/integration.rb
@@ -101,7 +101,7 @@ module ActionController
@https = flag
end
- # Return +true+ if the session is mimicing a secure HTTPS request.
+ # Return +true+ if the session is mimicking a secure HTTPS request.
#
# if session.https?
# ...
diff --git a/actionpack/lib/action_controller/rack_process.rb b/actionpack/lib/action_controller/rack_process.rb
index 01bc1ebb26..7e0a6b091e 100644
--- a/actionpack/lib/action_controller/rack_process.rb
+++ b/actionpack/lib/action_controller/rack_process.rb
@@ -24,7 +24,7 @@ module ActionController #:nodoc:
super()
end
- %w[ AUTH_TYPE CONTENT_TYPE GATEWAY_INTERFACE PATH_INFO
+ %w[ AUTH_TYPE GATEWAY_INTERFACE PATH_INFO
PATH_TRANSLATED QUERY_STRING REMOTE_HOST
REMOTE_IDENT REMOTE_USER SCRIPT_NAME
SERVER_NAME SERVER_PROTOCOL
@@ -98,10 +98,6 @@ module ActionController #:nodoc:
@env['REMOTE_ADDR']
end
- def request_method
- @env['REQUEST_METHOD'].downcase.to_sym
- end
-
def server_port
@env['SERVER_PORT'].to_i
end
@@ -250,11 +246,11 @@ end_msg
headers['Content-Language'] = options.delete('language') if options['language']
headers['Expires'] = options.delete('expires') if options['expires']
- @status = options['Status'] || "200 OK"
+ @status = options.delete('Status') || "200 OK"
# Convert 'cookie' header to 'Set-Cookie' headers.
# Because Set-Cookie header can appear more the once in the response body,
- # we store it in a line break seperated string that will be translated to
+ # we store it in a line break separated string that will be translated to
# multiple Set-Cookie header by the handler.
if cookie = options.delete('cookie')
cookies = []
diff --git a/actionpack/lib/action_controller/request.rb b/actionpack/lib/action_controller/request.rb
index 2d9f6c3e6f..c42f113d2c 100755
--- a/actionpack/lib/action_controller/request.rb
+++ b/actionpack/lib/action_controller/request.rb
@@ -61,7 +61,7 @@ module ActionController
request_method == :head
end
- # Provides acccess to the request's HTTP headers, for example:
+ # Provides access to the request's HTTP headers, for example:
# request.headers["Content-Type"] # => "text/plain"
def headers
@headers ||= ActionController::Http::Headers.new(@env)
@@ -265,7 +265,7 @@ EOM
parts[0..-(tld_length+2)]
end
- # Return the query string, accounting for server idiosyncracies.
+ # Return the query string, accounting for server idiosyncrasies.
def query_string
if uri = @env['REQUEST_URI']
uri.split('?', 2)[1] || ''
@@ -274,7 +274,7 @@ EOM
end
end
- # Return the request URI, accounting for server idiosyncracies.
+ # Return the request URI, accounting for server idiosyncrasies.
# WEBrick includes the full URL. IIS leaves REQUEST_URI blank.
def request_uri
if uri = @env['REQUEST_URI']
diff --git a/actionpack/lib/action_controller/request_forgery_protection.rb b/actionpack/lib/action_controller/request_forgery_protection.rb
index 02c9d59d07..05a6d8bb79 100644
--- a/actionpack/lib/action_controller/request_forgery_protection.rb
+++ b/actionpack/lib/action_controller/request_forgery_protection.rb
@@ -17,7 +17,7 @@ module ActionController #:nodoc:
# forged link from another site, is done by embedding a token based on the session (which an attacker wouldn't know) in all
# forms and Ajax requests generated by Rails and then verifying the authenticity of that token in the controller. Only
# HTML/JavaScript requests are checked, so this will not protect your XML API (presumably you'll have a different authentication
- # scheme there anyway). Also, GET requests are not protected as these should be indempotent anyway.
+ # scheme there anyway). Also, GET requests are not protected as these should be idempotent anyway.
#
# This is turned on with the <tt>protect_from_forgery</tt> method, which will check the token and raise an
# ActionController::InvalidAuthenticityToken if it doesn't match what was expected. You can customize the error message in
diff --git a/actionpack/lib/action_controller/rescue.rb b/actionpack/lib/action_controller/rescue.rb
index 163ed87fbb..482ac7d7a4 100644
--- a/actionpack/lib/action_controller/rescue.rb
+++ b/actionpack/lib/action_controller/rescue.rb
@@ -112,19 +112,23 @@ module ActionController #:nodoc:
protected
# Exception handler called when the performance of an action raises an exception.
def rescue_action(exception)
- log_error(exception) if logger
- erase_results if performed?
+ if handler_for_rescue(exception)
+ rescue_action_with_handler(exception)
+ else
+ log_error(exception) if logger
+ erase_results if performed?
- # Let the exception alter the response if it wants.
- # For example, MethodNotAllowed sets the Allow header.
- if exception.respond_to?(:handle_response!)
- exception.handle_response!(response)
- end
+ # Let the exception alter the response if it wants.
+ # For example, MethodNotAllowed sets the Allow header.
+ if exception.respond_to?(:handle_response!)
+ exception.handle_response!(response)
+ end
- if consider_all_requests_local || local_request?
- rescue_action_locally(exception)
- else
- rescue_action_in_public(exception)
+ if consider_all_requests_local || local_request?
+ rescue_action_locally(exception)
+ else
+ rescue_action_in_public(exception)
+ end
end
end
@@ -200,7 +204,7 @@ module ActionController #:nodoc:
def perform_action_with_rescue #:nodoc:
perform_action_without_rescue
rescue Exception => exception
- rescue_action_with_handler(exception) || rescue_action(exception)
+ rescue_action(exception)
end
def rescues_path(template_name)
diff --git a/actionpack/lib/action_controller/resources.rb b/actionpack/lib/action_controller/resources.rb
index af2fcaf3ad..b11aa5625b 100644
--- a/actionpack/lib/action_controller/resources.rb
+++ b/actionpack/lib/action_controller/resources.rb
@@ -296,6 +296,10 @@ module ActionController
# article_comments_url(:article_id => @article)
# article_comment_url(:article_id => @article, :id => @comment)
#
+ # If you don't want to load all objects from the database you might want to use the <tt>article_id</tt> directly:
+ #
+ # articles_comments_url(@comment.article_id, @comment)
+ #
# * <tt>:name_prefix</tt> - Define a prefix for all generated routes, usually ending in an underscore.
# Use this if you have named routes that may clash.
#
diff --git a/actionpack/lib/action_controller/streaming.rb b/actionpack/lib/action_controller/streaming.rb
index 186e0e5531..333fb61b45 100644
--- a/actionpack/lib/action_controller/streaming.rb
+++ b/actionpack/lib/action_controller/streaming.rb
@@ -12,19 +12,21 @@ module ActionController #:nodoc:
X_SENDFILE_HEADER = 'X-Sendfile'.freeze
protected
- # Sends the file by streaming it 4096 bytes at a time. This way the
- # whole file doesn't need to be read into memory at once. This makes
- # it feasible to send even large files.
+ # Sends the file, by default streaming it 4096 bytes at a time. This way the
+ # whole file doesn't need to be read into memory at once. This makes it
+ # feasible to send even large files. You can optionally turn off streaming
+ # and send the whole file at once.
#
- # Be careful to sanitize the path parameter if it coming from a web
+ # Be careful to sanitize the path parameter if it is coming from a web
# page. <tt>send_file(params[:path])</tt> allows a malicious user to
# download any file on your server.
#
# Options:
# * <tt>:filename</tt> - suggests a filename for the browser to use.
# Defaults to <tt>File.basename(path)</tt>.
- # * <tt>:type</tt> - specifies an HTTP content type.
- # Defaults to 'application/octet-stream'.
+ # * <tt>:type</tt> - specifies an HTTP content type. Defaults to 'application/octet-stream'.
+ # * <tt>:length</tt> - used to manually override the length (in bytes) of the content that
+ # is going to be sent to the client. Defaults to <tt>File.size(path)</tt>.
# * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
# Valid values are 'inline' and 'attachment' (default).
# * <tt>:stream</tt> - whether to send the file to the user agent as it is read (+true+)
@@ -35,6 +37,12 @@ module ActionController #:nodoc:
# * <tt>:url_based_filename</tt> - set to +true+ if you want the browser guess the filename from
# the URL, which is necessary for i18n filenames on certain browsers
# (setting <tt>:filename</tt> overrides this option).
+ # * <tt>:x_sendfile</tt> - uses X-Sendfile to send the file when set to +true+. This is currently
+ # only available with Lighttpd/Apache2 and specific modules installed and activated. Since this
+ # uses the web server to send the file, this may lower memory consumption on your server and
+ # it will not block your application for further requests.
+ # See http://blog.lighttpd.net/articles/2006/07/02/x-sendfile and
+ # http://tn123.ath.cx/mod_xsendfile/ for details. Defaults to +false+.
#
# The default Content-Type and Content-Disposition headers are
# set to download arbitrary binary files in as many browsers as
@@ -99,8 +107,7 @@ module ActionController #:nodoc:
#
# Options:
# * <tt>:filename</tt> - suggests a filename for the browser to use.
- # * <tt>:type</tt> - specifies an HTTP content type.
- # Defaults to 'application/octet-stream'.
+ # * <tt>:type</tt> - specifies an HTTP content type. Defaults to 'application/octet-stream'.
# * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
# Valid values are 'inline' and 'attachment' (default).
# * <tt>:status</tt> - specifies the status code to send with the response. Defaults to '200 OK'.
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index 77c6f26eac..c09050c390 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -15,6 +15,27 @@ module ActionController
end
end
+ # Superclass for Action Controller functional tests. Infers the controller under test from the test class name,
+ # and creates @controller, @request, @response instance variables.
+ #
+ # class WidgetsControllerTest < ActionController::TestCase
+ # def test_index
+ # get :index
+ # end
+ # end
+ #
+ # * @controller - WidgetController.new
+ # * @request - ActionController::TestRequest.new
+ # * @response - ActionController::TestResponse.new
+ #
+ # (Earlier versions of Rails required each functional test to subclass Test::Unit::TestCase and define
+ # @controller, @request, @response in +setup+.)
+ #
+ # If the controller cannot be inferred from the test class name, you can explicity set it with +tests+.
+ #
+ # class SpecialEdgeCaseWidgetsControllerTest < ActionController::TestCase
+ # tests WidgetController
+ # end
class TestCase < ActiveSupport::TestCase
# When the request.remote_addr remains the default for testing, which is 0.0.0.0, the exception is simply raised inline
# (bystepping the regular exception handling from rescue_action). If the request.remote_addr is anything else, the regular
@@ -41,6 +62,8 @@ module ActionController
@@controller_class = nil
class << self
+ # Sets the controller class name. Useful if the name can't be inferred from test class.
+ # Expects +controller_class+ as a constant. Example: <tt>tests WidgetController</tt>.
def tests(controller_class)
self.controller_class = controller_class
end
diff --git a/actionpack/lib/action_controller/test_process.rb b/actionpack/lib/action_controller/test_process.rb
index a6e0c98936..721592b81f 100644
--- a/actionpack/lib/action_controller/test_process.rb
+++ b/actionpack/lib/action_controller/test_process.rb
@@ -171,7 +171,7 @@ module ActionController #:nodoc:
# Was the response successful?
def success?
- response_code == 200
+ (200..299).include?(response_code)
end
# Was the URL not found?
@@ -211,7 +211,7 @@ module ActionController #:nodoc:
template._first_render
end
- # A shortcut to the flash. Returns an empyt hash if no session flash exists.
+ # A shortcut to the flash. Returns an empty hash if no session flash exists.
def flash
session['flash'] || {}
end
diff --git a/actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb b/actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb
index 1a3c770254..376bb87409 100644
--- a/actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb
+++ b/actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb
@@ -64,7 +64,7 @@ module HTML
#
# When using a combination of the above, the element name comes first
# followed by identifier, class names, attributes, pseudo classes and
- # negation in any order. Do not seprate these parts with spaces!
+ # negation in any order. Do not separate these parts with spaces!
# Space separation is used for descendant selectors.
#
# For example:
@@ -158,7 +158,7 @@ module HTML
# * <tt>:not(selector)</tt> -- Match the element only if the element does not
# match the simple selector.
#
- # As you can see, <tt>:nth-child<tt> pseudo class and its varient can get quite
+ # As you can see, <tt>:nth-child<tt> pseudo class and its variant can get quite
# tricky and the CSS specification doesn't do a much better job explaining it.
# But after reading the examples and trying a few combinations, it's easy to
# figure out.
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb
index 04e8d3a358..85af73390d 100644
--- a/actionpack/lib/action_view/base.rb
+++ b/actionpack/lib/action_view/base.rb
@@ -171,12 +171,13 @@ module ActionView #:nodoc:
delegate :erb_trim_mode=, :to => 'ActionView::TemplateHandlers::ERB'
end
- # Specify whether templates should be cached. Otherwise the file we be read everytime it is accessed.
- @@cache_template_loading = false
- cattr_accessor :cache_template_loading
+ def self.cache_template_loading=(*args)
+ ActiveSupport::Deprecation.warn("config.action_view.cache_template_loading option has been deprecated and has no affect. " <<
+ "Please remove it from your config files.", caller)
+ end
def self.cache_template_extensions=(*args)
- ActiveSupport::Deprecation.warn("config.action_view.cache_template_extensions option has been deprecated and has no affect. " <<
+ ActiveSupport::Deprecation.warn("config.action_view.cache_template_extensions option has been deprecated and has no effect. " <<
"Please remove it from your config files.", caller)
end
diff --git a/actionpack/lib/action_view/helpers/active_record_helper.rb b/actionpack/lib/action_view/helpers/active_record_helper.rb
index aa978a33bd..959d8a8563 100644
--- a/actionpack/lib/action_view/helpers/active_record_helper.rb
+++ b/actionpack/lib/action_view/helpers/active_record_helper.rb
@@ -141,7 +141,7 @@ module ActionView
#
# error_messages_for 'user_common', 'user', :object_name => 'user'
#
- # If the objects cannot be located as instance variables, you can add an extra <tt>:object</tt> paremeter which gives the actual
+ # If the objects cannot be located as instance variables, you can add an extra <tt>:object</tt> parameter which gives the actual
# object (or array of objects to use):
#
# error_messages_for 'user', :object => @question.user
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
index bf13945844..ac71f83336 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -503,21 +503,24 @@ module ActionView
source = "#{@controller.request.relative_url_root}#{source}"
end
end
- source = rewrite_asset_path(source)
- if include_host
- host = compute_asset_host(source)
+ rewrite_asset_path(source)
+ end
+ end
- if has_request && !host.blank? && host !~ %r{^[-a-z]+://}
- host = "#{@controller.request.protocol}#{host}"
- end
+ source = ActionView::Base.computed_public_paths[cache_key]
- "#{host}#{source}"
- else
- source
- end
- end
+ if include_host && source !~ %r{^[-a-z]+://}
+ host = compute_asset_host(source)
+
+ if has_request && !host.blank? && host !~ %r{^[-a-z]+://}
+ host = "#{@controller.request.protocol}#{host}"
end
+
+ "#{host}#{source}"
+ else
+ source
+ end
end
# Pick an asset host for this source. Returns +nil+ if no host is set,
diff --git a/actionpack/lib/action_view/helpers/capture_helper.rb b/actionpack/lib/action_view/helpers/capture_helper.rb
index 720e2da8cc..e86ca27f31 100644
--- a/actionpack/lib/action_view/helpers/capture_helper.rb
+++ b/actionpack/lib/action_view/helpers/capture_helper.rb
@@ -122,14 +122,15 @@ module ActionView
nil
end
- private
- def with_output_buffer(buf = '')
- self.output_buffer, old_buffer = buf, output_buffer
- yield
- output_buffer
- ensure
- self.output_buffer = old_buffer
- end
+ # Use an alternate output buffer for the duration of the block.
+ # Defaults to a new empty string.
+ def with_output_buffer(buf = '') #:nodoc:
+ self.output_buffer, old_buffer = buf, output_buffer
+ yield
+ output_buffer
+ ensure
+ self.output_buffer = old_buffer
+ end
end
end
end
diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb
index 1f213127d3..2cdb9a224e 100755
--- a/actionpack/lib/action_view/helpers/date_helper.rb
+++ b/actionpack/lib/action_view/helpers/date_helper.rb
@@ -285,11 +285,11 @@ module ActionView
#
# # Generates a date select that discards the type of the field and defaults to the date in
# # my_date (six days after today)
- # select_datetime(my_date_time, :discard_type => true)
+ # select_date(my_date, :discard_type => true)
#
# # Generates a date select that defaults to the datetime in my_date (six days after today)
# # prefixed with 'payday' rather than 'date'
- # select_datetime(my_date_time, :prefix => 'payday')
+ # select_date(my_date, :prefix => 'payday')
#
def select_date(date = Date.current, options = {}, html_options = {})
options[:order] ||= []
diff --git a/actionpack/lib/action_view/helpers/debug_helper.rb b/actionpack/lib/action_view/helpers/debug_helper.rb
index 20de7e465f..90863fca08 100644
--- a/actionpack/lib/action_view/helpers/debug_helper.rb
+++ b/actionpack/lib/action_view/helpers/debug_helper.rb
@@ -2,21 +2,28 @@ module ActionView
module Helpers
# Provides a set of methods for making it easier to debug Rails objects.
module DebugHelper
- # Returns a <pre>-tag that has +object+ dumped by YAML. This creates a very
- # readable way to inspect an object.
+ # Returns a YAML representation of +object+ wrapped with <pre> and </pre>.
+ # If the object cannot be converted to YAML using +to_yaml+, +inspect+ will be called instead.
+ # Useful for inspecting an object at the time of rendering.
#
# ==== Example
- # my_hash = {'first' => 1, 'second' => 'two', 'third' => [1,2,3]}
- # debug(my_hash)
#
- # => <pre class='debug_dump'>---
- # first: 1
- # second: two
- # third:
- # - 1
- # - 2
- # - 3
- # </pre>
+ # @user = User.new({ :username => 'testing', :password => 'xyz', :age => 42}) %>
+ # debug(@user)
+ # # =>
+ # <pre class='debug_dump'>--- !ruby/object:User
+ # attributes:
+ # &nbsp; updated_at:
+ # &nbsp; username: testing
+ #
+ # &nbsp; age: 42
+ # &nbsp; password: xyz
+ # &nbsp; created_at:
+ # attributes_cache: {}
+ #
+ # new_record: true
+ # </pre>
+
def debug(object)
begin
Marshal::dump(object)
@@ -28,4 +35,4 @@ module ActionView
end
end
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index bafc635ad2..fa26aa4640 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -76,7 +76,7 @@ module ActionView
# Creates a form and a scope around a specific model object that is used as
# a base for questioning about values for the fields.
#
- # Rails provides succint resource-oriented form generation with +form_for+
+ # Rails provides succinct resource-oriented form generation with +form_for+
# like this:
#
# <% form_for @offer do |f| %>
@@ -449,8 +449,37 @@ module ActionView
# assigned to the template (identified by +object+). It's intended that +method+ returns an integer and if that
# integer is above zero, then the checkbox is checked. Additional options on the input tag can be passed as a
# hash with +options+. The +checked_value+ defaults to 1 while the default +unchecked_value+
- # is set to 0 which is convenient for boolean values. Since HTTP standards say that unchecked checkboxes don't post anything,
- # we add a hidden value with the same name as the checkbox as a work around.
+ # is set to 0 which is convenient for boolean values.
+ #
+ # ==== Gotcha
+ #
+ # The HTML specification says unchecked check boxes are not successful, and
+ # thus web browsers do not send them. Unfortunately this introduces a gotcha:
+ # if an Invoice model has a +paid+ flag, and in the form that edits a paid
+ # invoice the user unchecks its check box, no +paid+ parameter is sent. So,
+ # any mass-assignment idiom like
+ #
+ # @invoice.update_attributes(params[:invoice])
+ #
+ # wouldn't update the flag.
+ #
+ # To prevent this the helper generates a hidden field with the same name as
+ # the checkbox after the very check box. So, the client either sends only the
+ # hidden field (representing the check box is unchecked), or both fields.
+ # Since the HTML specification says key/value pairs have to be sent in the
+ # same order they appear in the form and Rails parameters extraction always
+ # gets the first occurrence of any given key, that works in ordinary forms.
+ #
+ # Unfortunately that workaround does not work when the check box goes
+ # within an array-like parameter, as in
+ #
+ # <% fields_for "project[invoice_attributes][]", invoice, :index => nil do |form| %>
+ # <%= form.check_box :paid %>
+ # ...
+ # <% end %>
+ #
+ # because parameter name repetition is precisely what Rails seeks to distinguish
+ # the elements of the array.
#
# ==== Examples
# # Let's say that @post.validated? is 1:
diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb
index 22bd5cb440..32089442b7 100644
--- a/actionpack/lib/action_view/helpers/javascript_helper.rb
+++ b/actionpack/lib/action_view/helpers/javascript_helper.rb
@@ -44,13 +44,22 @@ module ActionView
include PrototypeHelper
- # Returns a link that will trigger a JavaScript +function+ using the
+ # Returns a link of the given +name+ that will trigger a JavaScript +function+ using the
# onclick handler and return false after the fact.
#
+ # The first argument +name+ is used as the link text.
+ #
+ # The next arguments are optional and may include the javascript function definition and a hash of html_options.
+ #
# The +function+ argument can be omitted in favor of an +update_page+
# block, which evaluates to a string when the template is rendered
# (instead of making an Ajax request first).
#
+ # The +html_options+ will accept a hash of html attributes for the link tag. Some examples are :class => "nav_button", :id => "articles_nav_button"
+ #
+ # Note: if you choose to specify the javascript function in a block, but would like to pass html_options, set the +function+ parameter to nil
+ #
+ #
# Examples:
# link_to_function "Greeting", "alert('Hello world!')"
# Produces:
@@ -89,13 +98,21 @@ module ActionView
content_tag(:a, name, html_options.merge(:href => href, :onclick => onclick))
end
- # Returns a button that'll trigger a JavaScript +function+ using the
+ # Returns a button with the given +name+ text that'll trigger a JavaScript +function+ using the
# onclick handler.
#
+ # The first argument +name+ is used as the button's value or display text.
+ #
+ # The next arguments are optional and may include the javascript function definition and a hash of html_options.
+ #
# The +function+ argument can be omitted in favor of an +update_page+
# block, which evaluates to a string when the template is rendered
# (instead of making an Ajax request first).
#
+ # The +html_options+ will accept a hash of html attributes for the link tag. Some examples are :class => "nav_button", :id => "articles_nav_button"
+ #
+ # Note: if you choose to specify the javascript function in a block, but would like to pass html_options, set the +function+ parameter to nil
+ #
# Examples:
# button_to_function "Greeting", "alert('Hello world!')"
# button_to_function "Delete", "if (confirm('Really?')) do_delete()"
diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb
index edb43844a4..cb4b53a9f7 100644
--- a/actionpack/lib/action_view/helpers/prototype_helper.rb
+++ b/actionpack/lib/action_view/helpers/prototype_helper.rb
@@ -61,7 +61,7 @@ module ActionView
#
# == Designing your Rails actions for Ajax
# When building your action handlers (that is, the Rails actions that receive your background requests), it's
- # important to remember a few things. First, whatever your action would normall return to the browser, it will
+ # important to remember a few things. First, whatever your action would normally return to the browser, it will
# return to the Ajax call. As such, you typically don't want to render with a layout. This call will cause
# the layout to be transmitted back to your page, and, if you have a full HTML/CSS, will likely mess a lot of things up.
# You can turn the layout off on particular actions by doing the following:
@@ -580,9 +580,10 @@ module ActionView
class JavaScriptGenerator #:nodoc:
def initialize(context, &block) #:nodoc:
@context, @lines = context, []
- @context.output_buffer = @lines if @context
include_helpers_from_context
- @context.instance_exec(self, &block)
+ @context.with_output_buffer(@lines) do
+ @context.instance_exec(self, &block)
+ end
end
private
diff --git a/actionpack/lib/action_view/helpers/sanitize_helper.rb b/actionpack/lib/action_view/helpers/sanitize_helper.rb
index b0dacfe964..c3c03394ee 100644
--- a/actionpack/lib/action_view/helpers/sanitize_helper.rb
+++ b/actionpack/lib/action_view/helpers/sanitize_helper.rb
@@ -184,7 +184,7 @@ module ActionView
HTML::WhiteListSanitizer.allowed_attributes.merge(attributes)
end
- # Adds to the Set of allowed CSS properties for the #sanitize and +sanitize_css+ heleprs.
+ # Adds to the Set of allowed CSS properties for the #sanitize and +sanitize_css+ helpers.
#
# Rails::Initializer.run do |config|
# config.action_view.sanitized_allowed_css_properties = 'expression'
diff --git a/actionpack/lib/action_view/helpers/scriptaculous_helper.rb b/actionpack/lib/action_view/helpers/scriptaculous_helper.rb
index c9b2761cb8..b938c1a801 100644
--- a/actionpack/lib/action_view/helpers/scriptaculous_helper.rb
+++ b/actionpack/lib/action_view/helpers/scriptaculous_helper.rb
@@ -193,7 +193,7 @@ module ActionView
#
# * <tt>:onDrop</tt> - Called when a +draggable_element+ is dropped onto
# this element. Override this callback with a JavaScript expression to
- # change the default drop behavour. Example:
+ # change the default drop behaviour. Example:
#
# :onDrop => "function(draggable_element, droppable_element, event) { alert('I like bananas') }"
#
diff --git a/actionpack/lib/action_view/helpers/tag_helper.rb b/actionpack/lib/action_view/helpers/tag_helper.rb
index 5a296da247..de08672d2d 100644
--- a/actionpack/lib/action_view/helpers/tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/tag_helper.rb
@@ -64,7 +64,7 @@ module ActionView
# <% content_tag :div, :class => "strong" do -%>
# Hello world!
# <% end -%>
- # # => <div class="strong"><p>Hello world!</p></div>
+ # # => <div class="strong">Hello world!</div>
def content_tag(name, content_or_options_with_block = nil, options = nil, escape = true, &block)
if block_given?
options = content_or_options_with_block if content_or_options_with_block.is_a?(Hash)
diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb
index e5178938fd..94e1f1d33a 100644
--- a/actionpack/lib/action_view/helpers/url_helper.rb
+++ b/actionpack/lib/action_view/helpers/url_helper.rb
@@ -185,7 +185,7 @@ module ActionView
# link_to "Nonsense search", searches_path(:foo => "bar", :baz => "quux")
# # => <a href="/searches?foo=bar&amp;baz=quux">Nonsense search</a>
#
- # The three options specfic to +link_to+ (<tt>:confirm</tt>, <tt>:popup</tt>, and <tt>:method</tt>) are used as follows:
+ # The three options specific to +link_to+ (<tt>:confirm</tt>, <tt>:popup</tt>, and <tt>:method</tt>) are used as follows:
#
# link_to "Visit Other Site", "http://www.rubyonrails.org/", :confirm => "Are you sure?"
# # => <a href="http://www.rubyonrails.org/" onclick="return confirm('Are you sure?');">Visit Other Site</a>
diff --git a/actionpack/lib/action_view/paths.rb b/actionpack/lib/action_view/paths.rb
index b0ab7d0c67..c7a5df762f 100644
--- a/actionpack/lib/action_view/paths.rb
+++ b/actionpack/lib/action_view/paths.rb
@@ -16,6 +16,14 @@ module ActionView #:nodoc:
end
class Path #:nodoc:
+ def self.eager_load_templates!
+ @eager_load_templates = true
+ end
+
+ def self.eager_load_templates?
+ @eager_load_templates || false
+ end
+
attr_reader :path, :paths
delegate :to_s, :to_str, :inspect, :to => :path
@@ -37,6 +45,9 @@ module ActionView #:nodoc:
@paths = {}
templates_in_path do |template|
+ # Eager load memoized methods and freeze cached template
+ template.freeze if self.class.eager_load_templates?
+
@paths[template.path] = template
@paths[template.path_without_extension] ||= template
end
@@ -48,10 +59,7 @@ module ActionView #:nodoc:
def templates_in_path
(Dir.glob("#{@path}/**/*/**") | Dir.glob("#{@path}/**")).each do |file|
unless File.directory?(file)
- template = Template.new(file.split("#{self}/").last, self)
- # Eager load memoized methods and freeze cached template
- template.freeze if Base.cache_template_loading
- yield template
+ yield Template.new(file.split("#{self}/").last, self)
end
end
end
diff --git a/actionpack/lib/action_view/renderable.rb b/actionpack/lib/action_view/renderable.rb
index f66356c939..46193670f3 100644
--- a/actionpack/lib/action_view/renderable.rb
+++ b/actionpack/lib/action_view/renderable.rb
@@ -9,6 +9,10 @@ module ActionView
include ActiveSupport::Memoizable
+ def filename
+ 'compiled-template'
+ end
+
def handler
Template.handler_class_for_extension(extension)
end
@@ -35,35 +39,41 @@ module ActionView
end
private
- # Compile and evaluate the template's code
+ # Compile and evaluate the template's code (if necessary)
def compile(local_assigns)
render_symbol = method(local_assigns)
@@mutex.synchronize do
- return false unless recompile?(render_symbol)
+ if recompile?(render_symbol)
+ compile!(render_symbol, local_assigns)
+ end
+ end
+ end
- locals_code = local_assigns.keys.map { |key| "#{key} = local_assigns[:#{key}];" }.join
+ def compile!(render_symbol, local_assigns)
+ locals_code = local_assigns.keys.map { |key| "#{key} = local_assigns[:#{key}];" }.join
- source = <<-end_src
- def #{render_symbol}(local_assigns)
- old_output_buffer = output_buffer;#{locals_code};#{compiled_source}
- ensure
- self.output_buffer = old_output_buffer
- end
- end_src
+ source = <<-end_src
+ def #{render_symbol}(local_assigns)
+ old_output_buffer = output_buffer;#{locals_code};#{compiled_source}
+ ensure
+ self.output_buffer = old_output_buffer
+ end
+ end_src
- begin
- file_name = respond_to?(:filename) ? filename : 'compiled-template'
- ActionView::Base::CompiledTemplates.module_eval(source, file_name, 0)
- rescue Exception => e # errors from template code
- if logger = ActionController::Base.logger
- logger.debug "ERROR: compiling #{render_symbol} RAISED #{e}"
- logger.debug "Function body: #{source}"
- logger.debug "Backtrace: #{e.backtrace.join("\n")}"
- end
+ begin
+ logger = ActionController::Base.logger
+ logger.debug "Compiling template #{render_symbol}" if logger
- raise ActionView::TemplateError.new(self, {}, e)
+ ActionView::Base::CompiledTemplates.module_eval(source, filename, 0)
+ rescue Exception => e # errors from template code
+ if logger
+ logger.debug "ERROR: compiling #{render_symbol} RAISED #{e}"
+ logger.debug "Function body: #{source}"
+ logger.debug "Backtrace: #{e.backtrace.join("\n")}"
end
+
+ raise ActionView::TemplateError.new(self, {}, e)
end
end
@@ -71,8 +81,7 @@ module ActionView
# The template will be compiled if the file has not been compiled yet, or
# if local_assigns has a new key, which isn't supported by the compiled code yet.
def recompile?(symbol)
- meth = Base::CompiledTemplates.instance_method(template.method) rescue nil
- !(meth && Base.cache_template_loading)
+ !(frozen? && Base::CompiledTemplates.method_defined?(symbol))
end
end
end
diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb
index 0d2e0f273a..9db4cddd6a 100644
--- a/actionpack/test/abstract_unit.rb
+++ b/actionpack/test/abstract_unit.rb
@@ -22,8 +22,8 @@ ActiveSupport::Deprecation.debug = true
ActionController::Base.logger = nil
ActionController::Routing::Routes.reload rescue nil
-ActionView::Base.cache_template_loading = true
FIXTURE_LOAD_PATH = File.join(File.dirname(__FILE__), 'fixtures')
+ActionView::PathSet::Path.eager_load_templates!
ActionController::Base.view_paths = FIXTURE_LOAD_PATH
# Wrap tests that use Mocha and skip if unavailable.
diff --git a/actionpack/test/controller/base_test.rb b/actionpack/test/controller/base_test.rb
index 34c0200fe8..d49cc2a9aa 100644
--- a/actionpack/test/controller/base_test.rb
+++ b/actionpack/test/controller/base_test.rb
@@ -7,6 +7,7 @@ module Submodule
end
class ContainedNonEmptyController < ActionController::Base
def public_action
+ render :nothing => true
end
hide_action :hidden_action
@@ -105,6 +106,18 @@ end
class PerformActionTest < Test::Unit::TestCase
+ class MockLogger
+ attr_reader :logged
+
+ def initialize
+ @logged = []
+ end
+
+ def method_missing(method, *args)
+ @logged << args.first
+ end
+ end
+
def use_controller(controller_class)
@controller = controller_class.new
@@ -142,6 +155,13 @@ class PerformActionTest < Test::Unit::TestCase
get :another_hidden_action
assert_response 404
end
+
+ def test_namespaced_action_should_log_module_name
+ use_controller Submodule::ContainedNonEmptyController
+ @controller.logger = MockLogger.new
+ get :public_action
+ assert_match /Processing\sSubmodule::ContainedNonEmptyController#public_action/, @controller.logger.logged[1]
+ end
end
class DefaultUrlOptionsTest < Test::Unit::TestCase
diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb
index 8f53ecd178..47a0fcf99d 100644
--- a/actionpack/test/controller/caching_test.rb
+++ b/actionpack/test/controller/caching_test.rb
@@ -148,7 +148,6 @@ class PageCachingTest < Test::Unit::TestCase
end
end
-
class ActionCachingTestController < ActionController::Base
caches_action :index, :redirected, :forbidden, :if => Proc.new { |c| !c.request.format.json? }, :expires_in => 1.hour
caches_action :show, :cache_path => 'http://test.host/custom/show'
@@ -489,54 +488,54 @@ class FragmentCachingTest < Test::Unit::TestCase
def test_fragment_cache_key
assert_equal 'views/what a key', @controller.fragment_cache_key('what a key')
- assert_equal( "views/test.host/fragment_caching_test/some_action",
- @controller.fragment_cache_key(:controller => 'fragment_caching_test',:action => 'some_action'))
+ assert_equal "views/test.host/fragment_caching_test/some_action",
+ @controller.fragment_cache_key(:controller => 'fragment_caching_test',:action => 'some_action')
end
- def test_read_fragment__with_caching_enabled
+ def test_read_fragment_with_caching_enabled
@store.write('views/name', 'value')
assert_equal 'value', @controller.read_fragment('name')
end
- def test_read_fragment__with_caching_disabled
+ def test_read_fragment_with_caching_disabled
ActionController::Base.perform_caching = false
@store.write('views/name', 'value')
assert_nil @controller.read_fragment('name')
end
- def test_fragment_exist__with_caching_enabled
+ def test_fragment_exist_with_caching_enabled
@store.write('views/name', 'value')
assert @controller.fragment_exist?('name')
assert !@controller.fragment_exist?('other_name')
end
- def test_fragment_exist__with_caching_disabled
+ def test_fragment_exist_with_caching_disabled
ActionController::Base.perform_caching = false
@store.write('views/name', 'value')
assert !@controller.fragment_exist?('name')
assert !@controller.fragment_exist?('other_name')
end
- def test_write_fragment__with_caching_enabled
+ def test_write_fragment_with_caching_enabled
assert_nil @store.read('views/name')
assert_equal 'value', @controller.write_fragment('name', 'value')
assert_equal 'value', @store.read('views/name')
end
- def test_write_fragment__with_caching_disabled
+ def test_write_fragment_with_caching_disabled
assert_nil @store.read('views/name')
ActionController::Base.perform_caching = false
assert_equal nil, @controller.write_fragment('name', 'value')
assert_nil @store.read('views/name')
end
- def test_expire_fragment__with_simple_key
+ def test_expire_fragment_with_simple_key
@store.write('views/name', 'value')
@controller.expire_fragment 'name'
assert_nil @store.read('views/name')
end
- def test_expire_fragment__with__regexp
+ def test_expire_fragment_with_regexp
@store.write('views/name', 'value')
@store.write('views/another_name', 'another_value')
@store.write('views/primalgrasp', 'will not expire ;-)')
@@ -548,7 +547,7 @@ class FragmentCachingTest < Test::Unit::TestCase
assert_equal 'will not expire ;-)', @store.read('views/primalgrasp')
end
- def test_fragment_for__with_disabled_caching
+ def test_fragment_for_with_disabled_caching
ActionController::Base.perform_caching = false
@store.write('views/expensive', 'fragment content')
@@ -573,7 +572,6 @@ class FragmentCachingTest < Test::Unit::TestCase
end
end
-
class FunctionalCachingController < ActionController::Base
def fragment_cached
end
@@ -590,6 +588,13 @@ class FunctionalCachingController < ActionController::Base
end
end
+ def formatted_fragment_cached
+ respond_to do |format|
+ format.html
+ format.xml
+ format.js
+ end
+ end
def rescue_action(e)
raise e
@@ -639,4 +644,35 @@ CACHED
assert_match /Fragment caching in a partial/, @response.body
assert_match "Fragment caching in a partial", @store.read('views/test.host/functional_caching/js_fragment_cached_with_partial')
end
+
+ def test_html_formatted_fragment_caching
+ get :formatted_fragment_cached, :format => "html"
+ assert_response :success
+ expected_body = "<body>\n<p>ERB</p>\n</body>"
+
+ assert_equal expected_body, @response.body
+
+ assert_equal "<p>ERB</p>", @store.read('views/test.host/functional_caching/formatted_fragment_cached')
+ end
+
+ def test_xml_formatted_fragment_caching
+ get :formatted_fragment_cached, :format => "xml"
+ assert_response :success
+ expected_body = "<body>\n <p>Builder</p>\n</body>\n"
+
+ assert_equal expected_body, @response.body
+
+ assert_equal " <p>Builder</p>\n", @store.read('views/test.host/functional_caching/formatted_fragment_cached')
+ end
+
+ def test_js_formatted_fragment_caching
+ get :formatted_fragment_cached, :format => "js"
+ assert_response :success
+ expected_body = %(title = "Hey";\n$("element_1").visualEffect("highlight");\n) +
+ %($("element_2").visualEffect("highlight");\nfooter = "Bye";)
+ assert_equal expected_body, @response.body
+
+ assert_equal ['$("element_1").visualEffect("highlight");', '$("element_2").visualEffect("highlight");'],
+ @store.read('views/test.host/functional_caching/formatted_fragment_cached')
+ end
end
diff --git a/actionpack/test/controller/cgi_test.rb b/actionpack/test/controller/cgi_test.rb
index bf3b8b788e..8ca70f8595 100755
--- a/actionpack/test/controller/cgi_test.rb
+++ b/actionpack/test/controller/cgi_test.rb
@@ -53,6 +53,15 @@ class BaseCgiTest < Test::Unit::TestCase
end
def default_test; end
+
+ private
+
+ def set_content_data(data)
+ @request.env['REQUEST_METHOD'] = 'POST'
+ @request.env['CONTENT_LENGTH'] = data.length
+ @request.env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded; charset=utf-8'
+ @request.env['RAW_POST_DATA'] = data
+ end
end
class CgiRequestTest < BaseCgiTest
@@ -155,10 +164,8 @@ end
class CgiRequestParamsParsingTest < BaseCgiTest
def test_doesnt_break_when_content_type_has_charset
- data = 'flamenco=love'
- @request.env['CONTENT_LENGTH'] = data.length
- @request.env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded; charset=utf-8'
- @request.env['RAW_POST_DATA'] = data
+ set_content_data 'flamenco=love'
+
assert_equal({"flamenco"=> "love"}, @request.request_parameters)
end
@@ -168,6 +175,41 @@ class CgiRequestParamsParsingTest < BaseCgiTest
end
end
+class CgiRequestContentTypeTest < BaseCgiTest
+ def test_html_content_type_verification
+ @request.env['CONTENT_TYPE'] = Mime::HTML.to_s
+ assert @request.content_type.verify_request?
+ end
+
+ def test_xml_content_type_verification
+ @request.env['CONTENT_TYPE'] = Mime::XML.to_s
+ assert !@request.content_type.verify_request?
+ end
+end
+
+class CgiRequestMethodTest < BaseCgiTest
+ def test_get
+ assert_equal :get, @request.request_method
+ end
+
+ def test_post
+ @request.env['REQUEST_METHOD'] = 'POST'
+ assert_equal :post, @request.request_method
+ end
+
+ def test_put
+ set_content_data '_method=put'
+
+ assert_equal :put, @request.request_method
+ end
+
+ def test_delete
+ set_content_data '_method=delete'
+
+ assert_equal :delete, @request.request_method
+ end
+end
+
class CgiRequestNeedsRewoundTest < BaseCgiTest
def test_body_should_be_rewound
data = 'foo'
diff --git a/actionpack/test/controller/cookie_test.rb b/actionpack/test/controller/cookie_test.rb
index b45fbb17d3..5a6fb49861 100644
--- a/actionpack/test/controller/cookie_test.rb
+++ b/actionpack/test/controller/cookie_test.rb
@@ -60,7 +60,7 @@ class CookieTest < Test::Unit::TestCase
end
def test_setting_cookie_for_fourteen_days_with_symbols
- get :authenticate_for_fourteen_days
+ get :authenticate_for_fourteen_days_with_symbols
assert_equal [ CGI::Cookie::new("name" => "user_name", "value" => "david", "expires" => Time.local(2005, 10, 10)) ], @response.headers["cookie"]
end
diff --git a/actionpack/test/controller/rack_test.rb b/actionpack/test/controller/rack_test.rb
index 486fe49737..ab8bbc3bf9 100644
--- a/actionpack/test/controller/rack_test.rb
+++ b/actionpack/test/controller/rack_test.rb
@@ -51,6 +51,15 @@ class BaseRackTest < Test::Unit::TestCase
end
def default_test; end
+
+ private
+
+ def set_content_data(data)
+ @request.env['REQUEST_METHOD'] = 'POST'
+ @request.env['CONTENT_LENGTH'] = data.length
+ @request.env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded; charset=utf-8'
+ @request.env['RAW_POST_DATA'] = data
+ end
end
class RackRequestTest < BaseRackTest
@@ -153,10 +162,8 @@ end
class RackRequestParamsParsingTest < BaseRackTest
def test_doesnt_break_when_content_type_has_charset
- data = 'flamenco=love'
- @request.env['CONTENT_LENGTH'] = data.length
- @request.env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded; charset=utf-8'
- @request.env['RAW_POST_DATA'] = data
+ set_content_data 'flamenco=love'
+
assert_equal({"flamenco"=> "love"}, @request.request_parameters)
end
@@ -166,6 +173,41 @@ class RackRequestParamsParsingTest < BaseRackTest
end
end
+class RackRequestContentTypeTest < BaseRackTest
+ def test_html_content_type_verification
+ @request.env['CONTENT_TYPE'] = Mime::HTML.to_s
+ assert @request.content_type.verify_request?
+ end
+
+ def test_xml_content_type_verification
+ @request.env['CONTENT_TYPE'] = Mime::XML.to_s
+ assert !@request.content_type.verify_request?
+ end
+end
+
+class RackRequestMethodTest < BaseRackTest
+ def test_get
+ assert_equal :get, @request.request_method
+ end
+
+ def test_post
+ @request.env['REQUEST_METHOD'] = 'POST'
+ assert_equal :post, @request.request_method
+ end
+
+ def test_put
+ set_content_data '_method=put'
+
+ assert_equal :put, @request.request_method
+ end
+
+ def test_delete
+ set_content_data '_method=delete'
+
+ assert_equal :delete, @request.request_method
+ end
+end
+
class RackRequestNeedsRewoundTest < BaseRackTest
def test_body_should_be_rewound
data = 'foo'
@@ -234,3 +276,34 @@ class RackResponseTest < BaseRackTest
assert_equal ["Hello, World!"], parts
end
end
+
+class RackResponseHeadersTest < BaseRackTest
+ def setup
+ super
+ @response = ActionController::RackResponse.new(@request)
+ @output = StringIO.new('')
+ @response.headers['Status'] = 200
+ end
+
+ def test_content_type
+ [204, 304].each do |c|
+ @response.headers['Status'] = c
+ assert !response_headers.has_key?("Content-Type")
+ end
+
+ [200, 302, 404, 500].each do |c|
+ @response.headers['Status'] = c
+ assert response_headers.has_key?("Content-Type")
+ end
+ end
+
+ def test_status
+ assert !response_headers.has_key?('Status')
+ end
+
+ private
+
+ def response_headers
+ @response.out(@output)[1]
+ end
+end
diff --git a/actionpack/test/controller/rescue_test.rb b/actionpack/test/controller/rescue_test.rb
index 27fcc5e04c..da076d2090 100644
--- a/actionpack/test/controller/rescue_test.rb
+++ b/actionpack/test/controller/rescue_test.rb
@@ -62,6 +62,11 @@ class RescueController < ActionController::Base
render :text => exception.message
end
+ # This is a Dispatcher exception and should be in ApplicationController.
+ rescue_from ActionController::RoutingError do
+ render :text => 'no way'
+ end
+
def raises
render :text => 'already rendered'
raise "don't panic!"
@@ -378,6 +383,10 @@ class RescueTest < Test::Unit::TestCase
assert_equal "RescueController::ResourceUnavailableToRescueAsString", @response.body
end
+ def test_rescue_dispatcher_exceptions
+ RescueController.process_with_exception(@request, @response, ActionController::RoutingError.new("Route not found"))
+ assert_equal "no way", @response.body
+ end
protected
def with_all_requests_local(local = true)
diff --git a/actionpack/test/fixtures/functional_caching/formatted_fragment_cached.html.erb b/actionpack/test/fixtures/functional_caching/formatted_fragment_cached.html.erb
new file mode 100644
index 0000000000..d7f43ad95e
--- /dev/null
+++ b/actionpack/test/fixtures/functional_caching/formatted_fragment_cached.html.erb
@@ -0,0 +1,3 @@
+<body>
+<% cache do %><p>ERB</p><% end %>
+</body> \ No newline at end of file
diff --git a/actionpack/test/fixtures/functional_caching/formatted_fragment_cached.js.rjs b/actionpack/test/fixtures/functional_caching/formatted_fragment_cached.js.rjs
new file mode 100644
index 0000000000..057f15e62f
--- /dev/null
+++ b/actionpack/test/fixtures/functional_caching/formatted_fragment_cached.js.rjs
@@ -0,0 +1,6 @@
+page.assign 'title', 'Hey'
+cache do
+ page['element_1'].visual_effect :highlight
+ page['element_2'].visual_effect :highlight
+end
+page.assign 'footer', 'Bye'
diff --git a/actionpack/test/fixtures/functional_caching/formatted_fragment_cached.xml.builder b/actionpack/test/fixtures/functional_caching/formatted_fragment_cached.xml.builder
new file mode 100644
index 0000000000..efdcc28e0f
--- /dev/null
+++ b/actionpack/test/fixtures/functional_caching/formatted_fragment_cached.xml.builder
@@ -0,0 +1,5 @@
+xml.body do
+ cache do
+ xml.p "Builder"
+ end
+end
diff --git a/actionpack/test/template/compiled_templates_test.rb b/actionpack/test/template/compiled_templates_test.rb
new file mode 100644
index 0000000000..4b34827f91
--- /dev/null
+++ b/actionpack/test/template/compiled_templates_test.rb
@@ -0,0 +1,41 @@
+require 'abstract_unit'
+require 'controller/fake_models'
+
+uses_mocha 'TestTemplateRecompilation' do
+ class CompiledTemplatesTest < Test::Unit::TestCase
+ def setup
+ @view = ActionView::Base.new(ActionController::Base.view_paths, {})
+ @compiled_templates = ActionView::Base::CompiledTemplates
+ @compiled_templates.instance_methods.each do |m|
+ @compiled_templates.send(:remove_method, m) if m =~ /^_run_/
+ end
+ end
+
+ def test_template_gets_compiled
+ assert_equal 0, @compiled_templates.instance_methods.size
+ assert_equal "Hello world!", @view.render("test/hello_world.erb")
+ assert_equal 1, @compiled_templates.instance_methods.size
+ end
+
+ def test_template_gets_recompiled_when_using_different_keys_in_local_assigns
+ assert_equal 0, @compiled_templates.instance_methods.size
+ assert_equal "Hello world!", @view.render("test/hello_world.erb")
+ assert_equal "Hello world!", @view.render("test/hello_world.erb", {:foo => "bar"})
+ assert_equal 2, @compiled_templates.instance_methods.size
+ end
+
+ def test_compiled_template_will_not_be_recompiled_when_rendered_with_identical_local_assigns
+ assert_equal 0, @compiled_templates.instance_methods.size
+ assert_equal "Hello world!", @view.render("test/hello_world.erb")
+ ActionView::Template.any_instance.expects(:compile!).never
+ assert_equal "Hello world!", @view.render("test/hello_world.erb")
+ end
+
+ def test_compiled_template_will_always_be_recompiled_when_rendered_if_template_is_outside_cache
+ assert_equal 0, @compiled_templates.instance_methods.size
+ assert_equal "Hello world!", @view.render("#{FIXTURE_LOAD_PATH}/test/hello_world.erb")
+ ActionView::Template.any_instance.expects(:compile!).times(3)
+ 3.times { assert_equal "Hello world!", @view.render("#{FIXTURE_LOAD_PATH}/test/hello_world.erb") }
+ end
+ end
+end
diff --git a/actionpack/test/template/javascript_helper_test.rb b/actionpack/test/template/javascript_helper_test.rb
index 36dfeba5ed..d41111127b 100644
--- a/actionpack/test/template/javascript_helper_test.rb
+++ b/actionpack/test/template/javascript_helper_test.rb
@@ -3,7 +3,11 @@ require 'abstract_unit'
class JavaScriptHelperTest < ActionView::TestCase
tests ActionView::Helpers::JavaScriptHelper
- attr_accessor :output_buffer
+ attr_accessor :template_format, :output_buffer
+
+ def setup
+ @template = self
+ end
def test_escape_javascript
assert_equal '', escape_javascript(nil)
diff --git a/actionpack/test/template/prototype_helper_test.rb b/actionpack/test/template/prototype_helper_test.rb
index 5528430d80..92cc85703b 100644
--- a/actionpack/test/template/prototype_helper_test.rb
+++ b/actionpack/test/template/prototype_helper_test.rb
@@ -28,7 +28,7 @@ class PrototypeHelperBaseTest < ActionView::TestCase
attr_accessor :template_format, :output_buffer
def setup
- @template = nil
+ @template = self
@controller = Class.new do
def url_for(options)
if options.is_a?(String)
@@ -243,8 +243,12 @@ class PrototypeHelperTest < PrototypeHelperBaseTest
end
def test_update_page
+ old_output_buffer = output_buffer
+
block = Proc.new { |page| page.replace_html('foo', 'bar') }
assert_equal create_generator(&block).to_s, update_page(&block)
+
+ assert_equal old_output_buffer, output_buffer
end
def test_update_page_tag
diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG
index 95fdd93f65..90be9b700a 100644
--- a/activerecord/CHANGELOG
+++ b/activerecord/CHANGELOG
@@ -1,5 +1,7 @@
*Edge*
+* Set config.active_record.timestamped_migrations = false to have migrations with numeric prefix instead of UTC timestamp. #446. [Andrew Stone, Nik Wakelin]
+
* change_column_default preserves the not-null constraint. #617 [Tarmo Tänav]
* Fixed that create database statements would always include "DEFAULT NULL" (Nick Sieger) [#334]
diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb
index 174ec95de2..64888f9110 100644
--- a/activerecord/lib/active_record/association_preload.rb
+++ b/activerecord/lib/active_record/association_preload.rb
@@ -34,7 +34,7 @@ module ActiveRecord
class_to_reflection = {}
# Not all records have the same class, so group then preload
# group on the reflection itself so that if various subclass share the same association then we do not split them
- # unncessarily
+ # unnecessarily
records.group_by {|record| class_to_reflection[record.class] ||= record.class.reflections[association]}.each do |reflection, records|
raise ConfigurationError, "Association named '#{ association }' was not found; perhaps you misspelled it?" unless reflection
send("preload_#{reflection.macro}_association", records, reflection, preload_options)
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 7ad7802cbc..fd9a443eb9 100755
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -582,12 +582,13 @@ module ActiveRecord
# has_many :clients
# end
#
- # class Company < ActiveRecord::Base; end
+ # class Client < ActiveRecord::Base; end
# end
# end
#
- # When Firm#clients is called, it will in turn call <tt>MyApplication::Business::Company.find(firm.id)</tt>. If you want to associate
- # with a class in another module scope, this can be done by specifying the complete class name. Example:
+ # When <tt>Firm#clients</tt> is called, it will in turn call <tt>MyApplication::Business::Client.find_all_by_firm_id(firm.id)</tt>.
+ # If you want to associate with a class in another module scope, this can be done by specifying the complete class name.
+ # Example:
#
# module MyApplication
# module Business
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 8ca5a85ad8..4f5d72a0be 100755
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -6,7 +6,7 @@ module ActiveRecord #:nodoc:
class ActiveRecordError < StandardError
end
- # Raised when the single-table inheritance mechanism failes to locate the subclass
+ # Raised when the single-table inheritance mechanism fails to locate the subclass
# (for example due to improper usage of column that +inheritance_column+ points to).
class SubclassNotFound < ActiveRecordError #:nodoc:
end
@@ -97,7 +97,7 @@ module ActiveRecord #:nodoc:
class MissingAttributeError < NoMethodError
end
- # Raised when an error occured while doing a mass assignment to an attribute through the
+ # Raised when an error occurred while doing a mass assignment to an attribute through the
# <tt>attributes=</tt> method. The exception has an +attribute+ property that is the name of the
# offending attribute.
class AttributeAssignmentError < ActiveRecordError
@@ -271,7 +271,7 @@ module ActiveRecord #:nodoc:
# # Now 'Bob' exist and is an 'admin'
# User.find_or_create_by_name('Bob', :age => 40) { |u| u.admin = true }
#
- # Use the <tt>find_or_initialize_by_</tt> finder if you want to return a new record without saving it first. Protected attributes won't be setted unless they are given in a block. For example:
+ # Use the <tt>find_or_initialize_by_</tt> finder if you want to return a new record without saving it first. Protected attributes won't be set unless they are given in a block. For example:
#
# # No 'Winter' tag exists
# winter = Tag.find_or_initialize_by_name("Winter")
@@ -439,6 +439,10 @@ module ActiveRecord #:nodoc:
cattr_accessor :schema_format , :instance_writer => false
@@schema_format = :ruby
+ # Specify whether or not to use timestamps for migration numbers
+ cattr_accessor :timestamped_migrations , :instance_writer => false
+ @@timestamped_migrations = true
+
# Determine whether to store the full constant name including namespace when using STI
superclass_delegating_accessor :store_full_sti_class
self.store_full_sti_class = false
@@ -724,8 +728,7 @@ module ActiveRecord #:nodoc:
# ==== Attributes
#
# * +updates+ - A String of column and value pairs that will be set on any records that match conditions.
- # * +conditions+ - An SQL fragment like "administrator = 1" or [ "user_name = ?", username ].
- # See conditions in the intro for more info.
+ # * +conditions+ - An SQL fragment like "administrator = 1" or [ "user_name = ?", username ]. See conditions in the intro for more info.
# * +options+ - Additional options are <tt>:limit</tt> and/or <tt>:order</tt>, see the examples for usage.
#
# ==== Examples
diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb
index 1e385fb128..be2621fdb6 100755
--- a/activerecord/lib/active_record/callbacks.rb
+++ b/activerecord/lib/active_record/callbacks.rb
@@ -50,7 +50,7 @@ module ActiveRecord
#
# == Inheritable callback queues
#
- # Besides the overwriteable callback methods, it's also possible to register callbacks through the use of the callback macros.
+ # Besides the overwritable callback methods, it's also possible to register callbacks through the use of the callback macros.
# Their main advantage is that the macros add behavior into a callback queue that is kept intact down through an inheritance
# hierarchy. Example:
#
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 7d8530ebef..0f60a91ef1 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -383,7 +383,7 @@ module ActiveRecord
def add_column_options!(sql, options) #:nodoc:
sql << " DEFAULT #{quote(options[:default], options[:column])}" if options_include_default?(options)
- # must explcitly check for :null to allow change_column to work on migrations
+ # must explicitly check for :null to allow change_column to work on migrations
if options.has_key? :null
if options[:null] == false
sql << " NOT NULL"
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 6d16d72dea..6a20f41a4b 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -867,7 +867,7 @@ module ActiveRecord
end
private
- # The internal PostgreSQL identifer of the money data type.
+ # The internal PostgreSQL identifier of the money data type.
MONEY_COLUMN_TYPE_OID = 790 #:nodoc:
# Connects to a PostgreSQL server and sets up the adapter depending on the
diff --git a/activerecord/lib/active_record/dirty.rb b/activerecord/lib/active_record/dirty.rb
index a7d767486c..4ce0356457 100644
--- a/activerecord/lib/active_record/dirty.rb
+++ b/activerecord/lib/active_record/dirty.rb
@@ -62,7 +62,7 @@ module ActiveRecord
changed_attributes.keys
end
- # Map of changed attrs => [original value, new value]
+ # Map of changed attrs => [original value, new value].
# person.changes # => {}
# person.name = 'bob'
# person.changes # => { 'name' => ['bill', 'bob'] }
@@ -93,27 +93,27 @@ module ActiveRecord
end
private
- # Map of change attr => original value.
+ # Map of change <tt>attr => original value</tt>.
def changed_attributes
@changed_attributes ||= {}
end
- # Handle *_changed? for method_missing.
+ # Handle <tt>*_changed?</tt> for +method_missing+.
def attribute_changed?(attr)
changed_attributes.include?(attr)
end
- # Handle *_change for method_missing.
+ # Handle <tt>*_change</tt> for +method_missing+.
def attribute_change(attr)
[changed_attributes[attr], __send__(attr)] if attribute_changed?(attr)
end
- # Handle *_was for method_missing.
+ # Handle <tt>*_was</tt> for +method_missing+.
def attribute_was(attr)
attribute_changed?(attr) ? changed_attributes[attr] : __send__(attr)
end
- # Handle *_will_change! for method_missing.
+ # Handle <tt>*_will_change!</tt> for +method_missing+.
def attribute_will_change!(attr)
changed_attributes[attr] = clone_attribute_value(:read_attribute, attr)
end
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index e095b3c766..731a350854 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -238,6 +238,22 @@ module ActiveRecord
# lower than the current schema version: when migrating up, those
# never-applied "interleaved" migrations will be automatically applied, and
# when migrating down, never-applied "interleaved" migrations will be skipped.
+ #
+ # == Timestamped Migrations
+ #
+ # By default, Rails generates migrations that look like:
+ #
+ # 20080717013526_your_migration_name.rb
+ #
+ # The prefix is a generation timestamp (in UTC).
+ #
+ # If you'd prefer to use numeric prefixes, you can turn timestamped migrations
+ # off by setting:
+ #
+ # config.active_record.timestamped_migrations = false
+ #
+ # In environment.rb.
+ #
class Migration
@@verbose = true
cattr_accessor :verbose
diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb
index 25eecaf49f..3eec1305e4 100755
--- a/activerecord/lib/active_record/validations.rb
+++ b/activerecord/lib/active_record/validations.rb
@@ -1,5 +1,5 @@
module ActiveRecord
- # Raised by save! and create! when the record is invalid. Use the
+ # Raised by <tt>save!</tt> and <tt>create!</tt> when the record is invalid. Use the
# +record+ method to retrieve the record which did not validate.
# begin
# complex_operation_that_calls_save!_internally
@@ -33,7 +33,7 @@ module ActiveRecord
# Adds an error to the base object instead of any particular attribute. This is used
# to report errors that don't tie to any specific attribute, but rather to the object
# as a whole. These error messages don't get prepended with any field name when iterating
- # with each_full, so they should be complete sentences.
+ # with +each_full+, so they should be complete sentences.
def add_to_base(msg)
add(:base, msg)
end
@@ -87,7 +87,7 @@ module ActiveRecord
!@errors[attribute.to_s].nil?
end
- # Returns nil, if no errors are associated with the specified +attribute+.
+ # Returns +nil+, if no errors are associated with the specified +attribute+.
# Returns the error message, if one error is associated with the specified +attribute+.
# Returns an array of error messages, if more than one error is associated with the specified +attribute+.
#
@@ -108,7 +108,7 @@ module ActiveRecord
alias :[] :on
- # Returns errors assigned to the base object through add_to_base according to the normal rules of on(attribute).
+ # Returns errors assigned to the base object through +add_to_base+ according to the normal rules of <tt>on(attribute)</tt>.
def on_base
on(:base)
end
@@ -121,15 +121,15 @@ module ActiveRecord
# end
#
# company = Company.create(:address => '123 First St.')
- # company.errors.each{|attr,msg| puts "#{attr} - #{msg}" } # =>
- # name - is too short (minimum is 5 characters)
- # name - can't be blank
- # address - can't be blank
+ # company.errors.each{|attr,msg| puts "#{attr} - #{msg}" }
+ # # => name - is too short (minimum is 5 characters)
+ # # name - can't be blank
+ # # address - can't be blank
def each
@errors.each_key { |attr| @errors[attr].each { |msg| yield attr, msg } }
end
- # Yields each full error message added. So Person.errors.add("first_name", "can't be empty") will be returned
+ # Yields each full error message added. So <tt>Person.errors.add("first_name", "can't be empty")</tt> will be returned
# through iteration as "First name can't be empty".
#
# class Company < ActiveRecord::Base
@@ -138,10 +138,10 @@ module ActiveRecord
# end
#
# company = Company.create(:address => '123 First St.')
- # company.errors.each_full{|msg| puts msg } # =>
- # Name is too short (minimum is 5 characters)
- # Name can't be blank
- # Address can't be blank
+ # company.errors.each_full{|msg| puts msg }
+ # # => Name is too short (minimum is 5 characters)
+ # # Name can't be blank
+ # # Address can't be blank
def each_full
full_messages.each { |msg| yield msg }
end
@@ -201,13 +201,13 @@ module ActiveRecord
# end
#
# company = Company.create(:address => '123 First St.')
- # company.errors.to_xml # =>
- # <?xml version="1.0" encoding="UTF-8"?>
- # <errors>
- # <error>Name is too short (minimum is 5 characters)</error>
- # <error>Name can't be blank</error>
- # <error>Address can't be blank</error>
- # </errors>
+ # company.errors.to_xml
+ # # => <?xml version="1.0" encoding="UTF-8"?>
+ # # <errors>
+ # # <error>Name is too short (minimum is 5 characters)</error>
+ # # <error>Name can't be blank</error>
+ # # <error>Address can't be blank</error>
+ # # </errors>
def to_xml(options={})
options[:root] ||= "errors"
options[:indent] ||= 2
@@ -264,7 +264,7 @@ module ActiveRecord
# person.errors.on "phone_number" # => "has invalid format"
# person.errors.each_full { |msg| puts msg }
# # => "Last name can't be empty\n" +
- # "Phone number has invalid format"
+ # # "Phone number has invalid format"
#
# person.attributes = { "last_name" => "Heinemeier", "phone_number" => "555-555" }
# person.save # => true (and person is now saved in the database)
@@ -303,7 +303,7 @@ module ActiveRecord
:odd => 'odd?', :even => 'even?' }.freeze
# Adds a validation method or block to the class. This is useful when
- # overriding the +validate+ instance method becomes too unwieldly and
+ # overriding the +validate+ instance method becomes too unwieldy and
# you're looking for more descriptive declaration of your validations.
#
# This can be done with a symbol pointing to a method:
diff --git a/activeresource/README b/activeresource/README
index bcb7b3cbc7..924017a659 100644
--- a/activeresource/README
+++ b/activeresource/README
@@ -37,7 +37,7 @@ lifecycle methods that operate against a persistent store.
Person.exists?(1) #=> true
As you can see, the methods are quite similar to Active Record's methods for dealing with database
-records. But rather than dealing with
+records. But rather than dealing directly with a database record, you're dealing with HTTP resources (which may or may not be database records).
==== Protocol
diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb
index 347dbb82aa..492ab27bef 100644
--- a/activeresource/lib/active_resource/base.rb
+++ b/activeresource/lib/active_resource/base.rb
@@ -111,7 +111,7 @@ module ActiveResource
# over HTTPS.
#
# Note: Some values cannot be provided in the URL passed to site. e.g. email addresses
- # as usernames. In those situations you should use the seperate user and password option.
+ # as usernames. In those situations you should use the separate user and password option.
# == Errors & Validation
#
# Error handling and validation is handled in much the same manner as you're used to seeing in
@@ -200,7 +200,7 @@ module ActiveResource
cattr_accessor :logger
class << self
- # Gets the URI of the REST resources to map for this class. The site variable is required
+ # Gets the URI of the REST resources to map for this class. The site variable is required for
# Active Resource's mapping to work.
def site
# Not using superclass_delegating_reader because don't want subclasses to modify superclass instance
@@ -226,7 +226,7 @@ module ActiveResource
end
# Sets the URI of the REST resources to map for this class to the value in the +site+ argument.
- # The site variable is required Active Resource's mapping to work.
+ # The site variable is required for Active Resource's mapping to work.
def site=(site)
@connection = nil
if site.nil?
@@ -288,7 +288,7 @@ module ActiveResource
end
# Returns the current format, default is ActiveResource::Formats::XmlFormat.
- def format # :nodoc:
+ def format
read_inheritable_attribute("format") || ActiveResource::Formats[:xml]
end
@@ -298,7 +298,7 @@ module ActiveResource
@timeout = timeout
end
- # Gets tthe number of seconds after which requests to the REST API should time out.
+ # Gets the number of seconds after which requests to the REST API should time out.
def timeout
if defined?(@timeout)
@timeout
@@ -426,16 +426,16 @@ module ActiveResource
alias_method :set_primary_key, :primary_key= #:nodoc:
- # Create a new resource instance and request to the remote service
+ # Creates a new resource instance and makes a request to the remote service
# that it be saved, making it equivalent to the following simultaneous calls:
#
# ryan = Person.new(:first => 'ryan')
# ryan.save
#
- # The newly created resource is returned. If a failure has occurred an
- # exception will be raised (see save). If the resource is invalid and
- # has not been saved then valid? will return <tt>false</tt>,
- # while new? will still return <tt>true</tt>.
+ # Returns the newly created resource. If a failure has occurred an
+ # exception will be raised (see <tt>save</tt>). If the resource is invalid and
+ # has not been saved then <tt>valid?</tt> will return <tt>false</tt>,
+ # while <tt>new?</tt> will still return <tt>true</tt>.
#
# ==== Examples
# Person.create(:name => 'Jeremy', :email => 'myname@nospam.com', :enabled => true)
@@ -812,7 +812,7 @@ module ActiveResource
# Person.delete(guys_id)
# that_guy.exists? # => false
def exists?
- !new? && self.class.exists?(to_param, :params => prefix_options)
+ !new? && self.class.exists?(to_param, :params => prefix_options)
end
# A method to convert the the resource to an XML string.
diff --git a/activeresource/lib/active_resource/custom_methods.rb b/activeresource/lib/active_resource/custom_methods.rb
index 4c8699288c..770116ceb7 100644
--- a/activeresource/lib/active_resource/custom_methods.rb
+++ b/activeresource/lib/active_resource/custom_methods.rb
@@ -48,8 +48,8 @@ module ActiveResource
# # => [{:id => 1, :name => 'Ryan'}]
#
# Note: the objects returned from this method are not automatically converted
- # into Active Resource instances - they are ordinary Hashes. If you are expecting
- # Active Resource instances, use the <tt>find</tt> class method with the
+ # into ActiveResource::Base instances - they are ordinary Hashes. If you are expecting
+ # ActiveResource::Base instances, use the <tt>find</tt> class method with the
# <tt>:from</tt> option. For example:
#
# Person.find(:all, :from => :active)
diff --git a/activeresource/lib/active_resource/http_mock.rb b/activeresource/lib/active_resource/http_mock.rb
index 22f83ae910..554fc3bcfc 100644
--- a/activeresource/lib/active_resource/http_mock.rb
+++ b/activeresource/lib/active_resource/http_mock.rb
@@ -65,7 +65,7 @@ module ActiveResource
class << self
# Returns an array of all request objects that have been sent to the mock. You can use this to check
- # wether or not your model actually sent an HTTP request.
+ # if your model actually sent an HTTP request.
#
# ==== Example
# def setup
diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb
index b3769b812f..58958dccef 100644
--- a/activesupport/lib/active_support/cache/mem_cache_store.rb
+++ b/activesupport/lib/active_support/cache/mem_cache_store.rb
@@ -29,8 +29,8 @@ module ActiveSupport
nil
end
- # Set key = value. Pass :unless_exist => true if you don't
- # want to update the cache if the key is already set.
+ # Set key = value. Pass :unless_exist => true if you don't
+ # want to update the cache if the key is already set.
def write(key, value, options = nil)
super
method = options && options[:unless_exist] ? :add : :set
@@ -56,33 +56,33 @@ module ActiveSupport
!read(key, options).nil?
end
- def increment(key, amount = 1)
+ def increment(key, amount = 1)
log("incrementing", key, amount)
-
- response = @data.incr(key, amount)
+
+ response = @data.incr(key, amount)
response == Response::NOT_FOUND ? nil : response
- rescue MemCache::MemCacheError
+ rescue MemCache::MemCacheError
nil
end
def decrement(key, amount = 1)
log("decrement", key, amount)
-
- response = data.decr(key, amount)
+
+ response = @data.decr(key, amount)
response == Response::NOT_FOUND ? nil : response
- rescue MemCache::MemCacheError
+ rescue MemCache::MemCacheError
nil
- end
-
+ end
+
def delete_matched(matcher, options = nil)
super
raise "Not supported by Memcache"
- end
-
+ end
+
def clear
@data.flush_all
- end
-
+ end
+
def stats
@data.stats
end
diff --git a/activesupport/lib/active_support/core_ext/bigdecimal/conversions.rb b/activesupport/lib/active_support/core_ext/bigdecimal/conversions.rb
index d2b01b1b8d..94c7c779f7 100644
--- a/activesupport/lib/active_support/core_ext/bigdecimal/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/bigdecimal/conversions.rb
@@ -21,7 +21,7 @@ module ActiveSupport #:nodoc:
# This emits the number without any scientific notation.
# I prefer it to using self.to_f.to_s, which would lose precision.
#
- # Note that YAML allows that when reconsituting floats
+ # Note that YAML allows that when reconstituting floats
# to native types, some precision may get lost.
# There is no full precision real YAML tag that I am aware of.
str = self.to_s
diff --git a/activesupport/lib/active_support/core_ext/hash.rb b/activesupport/lib/active_support/core_ext/hash.rb
index 6cbd9dd378..a6065ab48e 100644
--- a/activesupport/lib/active_support/core_ext/hash.rb
+++ b/activesupport/lib/active_support/core_ext/hash.rb
@@ -1,10 +1,11 @@
-%w(keys indifferent_access reverse_merge conversions diff slice except).each do |ext|
+%w(keys indifferent_access deep_merge reverse_merge conversions diff slice except).each do |ext|
require "active_support/core_ext/hash/#{ext}"
end
class Hash #:nodoc:
include ActiveSupport::CoreExtensions::Hash::Keys
include ActiveSupport::CoreExtensions::Hash::IndifferentAccess
+ include ActiveSupport::CoreExtensions::Hash::DeepMerge
include ActiveSupport::CoreExtensions::Hash::ReverseMerge
include ActiveSupport::CoreExtensions::Hash::Conversions
include ActiveSupport::CoreExtensions::Hash::Diff
diff --git a/activesupport/lib/active_support/core_ext/hash/deep_merge.rb b/activesupport/lib/active_support/core_ext/hash/deep_merge.rb
new file mode 100644
index 0000000000..f8842ba57a
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/hash/deep_merge.rb
@@ -0,0 +1,23 @@
+module ActiveSupport #:nodoc:
+ module CoreExtensions #:nodoc:
+ module Hash #:nodoc:
+ # Allows for deep merging
+ module DeepMerge
+ # Returns a new hash with +self+ and +other_hash+ merged recursively.
+ def deep_merge(other_hash)
+ self.merge(other_hash) do |key, oldval, newval|
+ oldval = oldval.to_hash if oldval.respond_to?(:to_hash)
+ newval = newval.to_hash if newval.respond_to?(:to_hash)
+ oldval.class.to_s == 'Hash' && newval.class.to_s == 'Hash' ? oldval.deep_merge(newval) : newval
+ end
+ end
+
+ # Returns a new hash with +self+ and +other_hash+ merged recursively.
+ # Modifies the receiver in place.
+ def deep_merge!(other_hash)
+ replace(deep_merge(other_hash))
+ end
+ end
+ end
+ end
+end
diff --git a/activesupport/lib/active_support/core_ext/hash/except.rb b/activesupport/lib/active_support/core_ext/hash/except.rb
index bc97fa35a6..f26d01553d 100644
--- a/activesupport/lib/active_support/core_ext/hash/except.rb
+++ b/activesupport/lib/active_support/core_ext/hash/except.rb
@@ -13,7 +13,7 @@ module ActiveSupport #:nodoc:
clone.except!(*keys)
end
- # Replaces the hash without only the given keys.
+ # Replaces the hash without the given keys.
def except!(*keys)
keys.map! { |key| convert_key(key) } if respond_to?(:convert_key)
keys.each { |key| delete(key) }
diff --git a/activesupport/lib/active_support/core_ext/hash/reverse_merge.rb b/activesupport/lib/active_support/core_ext/hash/reverse_merge.rb
index 7af10846e7..546e261cc9 100644
--- a/activesupport/lib/active_support/core_ext/hash/reverse_merge.rb
+++ b/activesupport/lib/active_support/core_ext/hash/reverse_merge.rb
@@ -1,21 +1,28 @@
module ActiveSupport #:nodoc:
module CoreExtensions #:nodoc:
module Hash #:nodoc:
- # Allows for reverse merging where its the keys in the calling hash that wins over those in the <tt>other_hash</tt>.
- # This is particularly useful for initializing an incoming option hash with default values:
+ # Allows for reverse merging two hashes where the keys in the calling hash take precedence over those
+ # in the <tt>other_hash</tt>. This is particularly useful for initializing an option hash with default values:
#
# def setup(options = {})
# options.reverse_merge! :size => 25, :velocity => 10
# end
#
- # The default <tt>:size</tt> and <tt>:velocity</tt> is only set if the +options+ passed in doesn't already have those keys set.
+ # Using <tt>merge</tt>, the above example would look as follows:
+ #
+ # def setup(options = {})
+ # { :size => 25, :velocity => 10 }.merge(options)
+ # end
+ #
+ # The default <tt>:size</tt> and <tt>:velocity</tt> are only set if the +options+ hash passed in doesn't already
+ # have the respective key.
module ReverseMerge
- # Performs the opposite of merge, with the keys and values from the first hash taking precedence over the second.
+ # Performs the opposite of <tt>merge</tt>, with the keys and values from the first hash taking precedence over the second.
def reverse_merge(other_hash)
other_hash.merge(self)
end
- # Performs the opposite of merge, with the keys and values from the first hash taking precedence over the second.
+ # Performs the opposite of <tt>merge</tt>, with the keys and values from the first hash taking precedence over the second.
# Modifies the receiver in place.
def reverse_merge!(other_hash)
replace(reverse_merge(other_hash))
diff --git a/activesupport/lib/active_support/core_ext/hash/slice.rb b/activesupport/lib/active_support/core_ext/hash/slice.rb
index be4dec6e53..3f14470f36 100644
--- a/activesupport/lib/active_support/core_ext/hash/slice.rb
+++ b/activesupport/lib/active_support/core_ext/hash/slice.rb
@@ -9,6 +9,11 @@ module ActiveSupport #:nodoc:
# end
#
# search(options.slice(:mass, :velocity, :time))
+ #
+ # If you have an array of keys you want to limit to, you should splat them:
+ #
+ # valid_keys = [:mass, :velocity, :time]
+ # search(options.slice(*valid_keys))
module Slice
# Returns a new hash with only the given keys.
def slice(*keys)
diff --git a/activesupport/lib/active_support/core_ext/object.rb b/activesupport/lib/active_support/core_ext/object.rb
index bbc7d81672..0796a7b710 100644
--- a/activesupport/lib/active_support/core_ext/object.rb
+++ b/activesupport/lib/active_support/core_ext/object.rb
@@ -1,4 +1,5 @@
require 'active_support/core_ext/object/conversions'
require 'active_support/core_ext/object/extending'
require 'active_support/core_ext/object/instance_variables'
+require 'active_support/core_ext/object/metaclass'
require 'active_support/core_ext/object/misc'
diff --git a/activesupport/lib/active_support/core_ext/object/metaclass.rb b/activesupport/lib/active_support/core_ext/object/metaclass.rb
new file mode 100644
index 0000000000..169a76dfb7
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/object/metaclass.rb
@@ -0,0 +1,8 @@
+class Object
+ # Get object's meta (ghost, eigenclass, singleton) class
+ def metaclass
+ class << self
+ self
+ end
+ end
+end
diff --git a/activesupport/lib/active_support/core_ext/string/inflections.rb b/activesupport/lib/active_support/core_ext/string/inflections.rb
index a009d7c085..3bbad7dad8 100644
--- a/activesupport/lib/active_support/core_ext/string/inflections.rb
+++ b/activesupport/lib/active_support/core_ext/string/inflections.rb
@@ -24,8 +24,8 @@ module ActiveSupport #:nodoc:
#
# "posts".singularize # => "post"
# "octopi".singularize # => "octopus"
- # "sheep".singluarize # => "sheep"
- # "word".singluarize # => "word"
+ # "sheep".singularize # => "sheep"
+ # "word".singularize # => "word"
# "the blue mailmen".singularize # => "the blue mailman"
# "CamelOctopi".singularize # => "CamelOctopus"
def singularize
diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb
index 2cce782676..cd234c9b89 100644
--- a/activesupport/lib/active_support/core_ext/time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/time/calculations.rb
@@ -261,7 +261,7 @@ module ActiveSupport #:nodoc:
# Layers additional behavior on Time#<=> so that DateTime and ActiveSupport::TimeWithZone instances
# can be chronologically compared with a Time
def compare_with_coercion(other)
- # if other is an ActiveSupport::TimeWithZone, coerce a Time instance from it so we can do <=> comparision
+ # if other is an ActiveSupport::TimeWithZone, coerce a Time instance from it so we can do <=> comparison
other = other.comparable_time if other.respond_to?(:comparable_time)
if other.acts_like?(:date)
# other is a Date/DateTime, so coerce self #to_datetime and hand off to DateTime#<=>
diff --git a/activesupport/lib/active_support/json.rb b/activesupport/lib/active_support/json.rb
index 54a7becd0f..2bdb4a7b11 100644
--- a/activesupport/lib/active_support/json.rb
+++ b/activesupport/lib/active_support/json.rb
@@ -1,5 +1,5 @@
module ActiveSupport
- # If true, use ISO 8601 format for dates and times. Otherwise, fall back to the Active Support legacy format.
+ # If true, use ISO 8601 format for dates and times. Otherwise, fall back to the Active Support legacy format.
mattr_accessor :use_standard_json_time_format
class << self
diff --git a/activesupport/lib/active_support/json/encoders/date.rb b/activesupport/lib/active_support/json/encoders/date.rb
index cb9419d29d..1fc99c466f 100644
--- a/activesupport/lib/active_support/json/encoders/date.rb
+++ b/activesupport/lib/active_support/json/encoders/date.rb
@@ -1,7 +1,14 @@
class Date
- # Returns a JSON string representing the date.
+ # Returns a JSON string representing the date. If ActiveSupport.use_standard_json_time_format is set to true, the
+ # ISO 8601 format is used.
#
- # ==== Example:
+ # ==== Examples:
+ #
+ # # With ActiveSupport.use_standard_json_time_format = true
+ # Date.new(2005,2,1).to_json
+ # # => "2005-02-01"
+ #
+ # # With ActiveSupport.use_standard_json_time_format = false
# Date.new(2005,2,1).to_json
# # => "2005/02/01"
def to_json(options = nil)
diff --git a/activesupport/lib/active_support/json/encoders/date_time.rb b/activesupport/lib/active_support/json/encoders/date_time.rb
index a4a5efbfb1..e259930033 100644
--- a/activesupport/lib/active_support/json/encoders/date_time.rb
+++ b/activesupport/lib/active_support/json/encoders/date_time.rb
@@ -1,7 +1,14 @@
class DateTime
- # Returns a JSON string representing the datetime.
+ # Returns a JSON string representing the datetime. If ActiveSupport.use_standard_json_time_format is set to true, the
+ # ISO 8601 format is used.
#
- # ==== Example:
+ # ==== Examples:
+ #
+ # # With ActiveSupport.use_standard_json_time_format = true
+ # DateTime.civil(2005,2,1,15,15,10).to_json
+ # # => "2005-02-01T15:15:10+00:00"
+ #
+ # # With ActiveSupport.use_standard_json_time_format = false
# DateTime.civil(2005,2,1,15,15,10).to_json
# # => "2005/02/01 15:15:10 +0000"
def to_json(options = nil)
diff --git a/activesupport/lib/active_support/json/encoders/time.rb b/activesupport/lib/active_support/json/encoders/time.rb
index 57ed3c9e31..09fc614889 100644
--- a/activesupport/lib/active_support/json/encoders/time.rb
+++ b/activesupport/lib/active_support/json/encoders/time.rb
@@ -1,9 +1,16 @@
class Time
- # Returns a JSON string representing the time.
+ # Returns a JSON string representing the time. If ActiveSupport.use_standard_json_time_format is set to true, the
+ # ISO 8601 format is used.
#
- # ==== Example:
+ # ==== Examples:
+ #
+ # # With ActiveSupport.use_standard_json_time_format = true
+ # Time.utc(2005,2,1,15,15,10).to_json
+ # # => "2005-02-01T15:15:10Z"
+ #
+ # # With ActiveSupport.use_standard_json_time_format = false
# Time.utc(2005,2,1,15,15,10).to_json
- # # => 2005/02/01 15:15:10 +0000"
+ # # => "2005/02/01 15:15:10 +0000"
def to_json(options = nil)
if ActiveSupport.use_standard_json_time_format
xmlschema.inspect
diff --git a/activesupport/lib/active_support/option_merger.rb b/activesupport/lib/active_support/option_merger.rb
index 1a4ff9db9a..c77bca1ac9 100644
--- a/activesupport/lib/active_support/option_merger.rb
+++ b/activesupport/lib/active_support/option_merger.rb
@@ -10,16 +10,8 @@ module ActiveSupport
private
def method_missing(method, *arguments, &block)
- merge_argument_options! arguments
+ arguments << (arguments.last.respond_to?(:to_hash) ? @options.deep_merge(arguments.pop) : @options.dup)
@context.send!(method, *arguments, &block)
end
-
- def merge_argument_options!(arguments)
- arguments << if arguments.last.respond_to? :to_hash
- @options.merge(arguments.pop)
- else
- @options.dup
- end
- end
end
end
diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb
index e85bfe9b2e..4866fa0dc8 100644
--- a/activesupport/lib/active_support/time_with_zone.rb
+++ b/activesupport/lib/active_support/time_with_zone.rb
@@ -102,7 +102,19 @@ module ActiveSupport
"#{time.strftime("%Y-%m-%dT%H:%M:%S")}#{formatted_offset(true, 'Z')}"
end
alias_method :iso8601, :xmlschema
-
+
+ # Returns a JSON string representing the TimeWithZone. If ActiveSupport.use_standard_json_time_format is set to
+ # true, the ISO 8601 format is used.
+ #
+ # ==== Examples:
+ #
+ # # With ActiveSupport.use_standard_json_time_format = true
+ # Time.utc(2005,2,1,15,15,10).in_time_zone.to_json
+ # # => "2005-02-01T15:15:10Z"
+ #
+ # # With ActiveSupport.use_standard_json_time_format = false
+ # Time.utc(2005,2,1,15,15,10).in_time_zone.to_json
+ # # => "2005/02/01 15:15:10 +0000"
def to_json(options = nil)
if ActiveSupport.use_standard_json_time_format
xmlschema.inspect
diff --git a/activesupport/lib/active_support/vendor/builder-2.1.2/builder/xmlevents.rb b/activesupport/lib/active_support/vendor/builder-2.1.2/builder/xmlevents.rb
index 91fcd21e13..b373e4da3c 100644
--- a/activesupport/lib/active_support/vendor/builder-2.1.2/builder/xmlevents.rb
+++ b/activesupport/lib/active_support/vendor/builder-2.1.2/builder/xmlevents.rb
@@ -20,7 +20,7 @@ module Builder
# markup.
#
# Usage:
- # xe = Builder::XmlEvents.new(hander)
+ # xe = Builder::XmlEvents.new(handler)
# xe.title("HI") # Sends start_tag/end_tag/text messages to the handler.
#
# Indentation may also be selected by providing value for the
diff --git a/activesupport/lib/active_support/vendor/memcache-client-1.5.0/memcache.rb b/activesupport/lib/active_support/vendor/memcache-client-1.5.0/memcache.rb
index dda7f2c30e..30113201a6 100644
--- a/activesupport/lib/active_support/vendor/memcache-client-1.5.0/memcache.rb
+++ b/activesupport/lib/active_support/vendor/memcache-client-1.5.0/memcache.rb
@@ -119,7 +119,7 @@ class MemCache
# Valid options for +opts+ are:
#
# [:namespace] Prepends this value to all keys added or retrieved.
- # [:readonly] Raises an exeception on cache writes when true.
+ # [:readonly] Raises an exception on cache writes when true.
# [:multithread] Wraps cache access in a Mutex for thread safety.
#
# Other options are ignored.
@@ -207,7 +207,7 @@ class MemCache
end
##
- # Deceremets the value for +key+ by +amount+ and returns the new value.
+ # Decrements the value for +key+ by +amount+ and returns the new value.
# +key+ must already exist. If +key+ is not an integer, it is assumed to be
# 0. +key+ can not be decremented below 0.
diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/data_timezone.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/data_timezone.rb
index 5eccbdf0db..2510e90b5b 100644
--- a/activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/data_timezone.rb
+++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/data_timezone.rb
@@ -38,7 +38,7 @@ module TZInfo
# Returns the set of TimezonePeriod instances that are valid for the given
# local time as an array. If you just want a single period, use
- # period_for_local instead and specify how abiguities should be resolved.
+ # period_for_local instead and specify how ambiguities should be resolved.
# Raises PeriodNotFound if no periods are found for the given time.
def periods_for_local(local)
info.periods_for_local(local)
diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/data_timezone_info.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/data_timezone_info.rb
index a45d94554b..8829ba9cc8 100644
--- a/activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/data_timezone_info.rb
+++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/data_timezone_info.rb
@@ -66,7 +66,7 @@ module TZInfo
# ArgumentError will be raised if a transition is added out of order.
# offset_id refers to an id defined with offset. ArgumentError will be
# raised if the offset_id cannot be found. numerator_or_time and
- # denomiator specify the time the transition occurs as. See
+ # denominator specify the time the transition occurs as. See
# TimezoneTransitionInfo for more detail about specifying times.
def transition(year, month, offset_id, numerator_or_time, denominator = nil)
offset = @offsets[offset_id]
diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/linked_timezone.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/linked_timezone.rb
index f8ec4fca87..c757485b84 100644
--- a/activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/linked_timezone.rb
+++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/linked_timezone.rb
@@ -36,7 +36,7 @@ module TZInfo
# Returns the set of TimezonePeriod instances that are valid for the given
# local time as an array. If you just want a single period, use
- # period_for_local instead and specify how abiguities should be resolved.
+ # period_for_local instead and specify how ambiguities should be resolved.
# Raises PeriodNotFound if no periods are found for the given time.
def periods_for_local(local)
@linked_timezone.periods_for_local(local)
diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/timezone.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/timezone.rb
index f87fb6fb70..eeaa772d0f 100644
--- a/activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/timezone.rb
+++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/timezone.rb
@@ -121,7 +121,7 @@ module TZInfo
TimezoneProxy.new(identifier)
end
- # If identifier is nil calls super(), otherwise calls get. An identfier
+ # If identifier is nil calls super(), otherwise calls get. An identifier
# should always be passed in when called externally.
def self.new(identifier = nil)
if identifier
diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/timezone_transition_info.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/timezone_transition_info.rb
index 3fecb24f0d..781764f0b0 100644
--- a/activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/timezone_transition_info.rb
+++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/timezone_transition_info.rb
@@ -37,7 +37,7 @@ module TZInfo
attr_reader :numerator_or_time
protected :numerator_or_time
- # Either the denominotor of the DateTime if the transition time is defined
+ # Either the denominator of the DateTime if the transition time is defined
# as a DateTime, otherwise nil.
attr_reader :denominator
protected :denominator
diff --git a/activesupport/lib/active_support/vendor/xml-simple-1.0.11/xmlsimple.rb b/activesupport/lib/active_support/vendor/xml-simple-1.0.11/xmlsimple.rb
index 0de24c0eff..ec6dab513f 100644
--- a/activesupport/lib/active_support/vendor/xml-simple-1.0.11/xmlsimple.rb
+++ b/activesupport/lib/active_support/vendor/xml-simple-1.0.11/xmlsimple.rb
@@ -121,7 +121,7 @@ class XmlSimple
# Create a "global" cache.
@@cache = Cache.new
- # Creates and intializes a new XmlSimple object.
+ # Creates and initializes a new XmlSimple object.
#
# defaults::
# Default values for options.
@@ -497,7 +497,7 @@ class XmlSimple
}
end
- # Fold Hases containing a single anonymous Array up into just the Array.
+ # Fold Hashes containing a single anonymous Array up into just the Array.
if count == 1
anonymoustag = @options['anonymoustag']
if result.has_key?(anonymoustag) && result[anonymoustag].instance_of?(Array)
@@ -907,7 +907,7 @@ class XmlSimple
# Thanks to Norbert Gawor for a bugfix.
#
# value::
- # Value to be checked for emptyness.
+ # Value to be checked for emptiness.
def empty(value)
case value
when Hash
diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb
index 69028a123f..fc8ed45358 100644
--- a/activesupport/test/core_ext/hash_ext_test.rb
+++ b/activesupport/test/core_ext/hash_ext_test.rb
@@ -245,6 +245,16 @@ class HashExtTest < Test::Unit::TestCase
assert(!indiff.keys.any? {|k| k.kind_of? String}, "A key was converted to a string!")
end
+ def test_deep_merge
+ hash_1 = { :a => "a", :b => "b", :c => { :c1 => "c1", :c2 => "c2", :c3 => { :d1 => "d1" } } }
+ hash_2 = { :a => 1, :c => { :c1 => 2, :c3 => { :d2 => "d2" } } }
+ expected = { :a => 1, :b => "b", :c => { :c1 => 2, :c2 => "c2", :c3 => { :d1 => "d1", :d2 => "d2" } } }
+ assert_equal expected, hash_1.deep_merge(hash_2)
+
+ hash_1.deep_merge!(hash_2)
+ assert_equal expected, hash_1
+ end
+
def test_reverse_merge
defaults = { :a => "x", :b => "y", :c => 10 }.freeze
options = { :a => 1, :b => 2 }
@@ -282,6 +292,27 @@ class HashExtTest < Test::Unit::TestCase
assert_equal expected, original
end
+ def test_slice_with_an_array_key
+ original = { :a => 'x', :b => 'y', :c => 10, [:a, :b] => "an array key" }
+ expected = { [:a, :b] => "an array key", :c => 10 }
+
+ # Should return a new hash with only the given keys when given an array key.
+ assert_equal expected, original.slice([:a, :b], :c)
+ assert_not_equal expected, original
+
+ # Should replace the hash with only the given keys when given an array key.
+ assert_equal expected, original.slice!([:a, :b], :c)
+ assert_equal expected, original
+ end
+
+ def test_slice_with_splatted_keys
+ original = { :a => 'x', :b => 'y', :c => 10, [:a, :b] => "an array key" }
+ expected = { :a => 'x', :b => "y" }
+
+ # Should grab each of the splatted keys.
+ assert_equal expected, original.slice(*[:a, :b])
+ end
+
def test_indifferent_slice
original = { :a => 'x', :b => 'y', :c => 10 }.with_indifferent_access
expected = { :a => 'x', :b => 'y' }.with_indifferent_access
diff --git a/activesupport/test/core_ext/object_and_class_ext_test.rb b/activesupport/test/core_ext/object_and_class_ext_test.rb
index 16f4ab888e..b0a746fdc7 100644
--- a/activesupport/test/core_ext/object_and_class_ext_test.rb
+++ b/activesupport/test/core_ext/object_and_class_ext_test.rb
@@ -173,6 +173,14 @@ class ObjectTests < Test::Unit::TestCase
assert duck.acts_like?(:time)
assert !duck.acts_like?(:date)
end
+
+ def test_metaclass
+ string = "Hello"
+ string.metaclass.instance_eval do
+ define_method(:foo) { "bar" }
+ end
+ assert_equal "bar", string.foo
+ end
end
class ObjectInstanceVariableTest < Test::Unit::TestCase
diff --git a/activesupport/test/option_merger_test.rb b/activesupport/test/option_merger_test.rb
index 509c6d3bad..0d72314880 100644
--- a/activesupport/test/option_merger_test.rb
+++ b/activesupport/test/option_merger_test.rb
@@ -38,6 +38,33 @@ class OptionMergerTest < Test::Unit::TestCase
end
end
+ def test_nested_method_with_options_containing_hashes_merge
+ with_options :conditions => { :method => :get } do |outer|
+ outer.with_options :conditions => { :domain => "www" } do |inner|
+ expected = { :conditions => { :method => :get, :domain => "www" } }
+ assert_equal expected, inner.method_with_options
+ end
+ end
+ end
+
+ def test_nested_method_with_options_containing_hashes_overwrite
+ with_options :conditions => { :method => :get, :domain => "www" } do |outer|
+ outer.with_options :conditions => { :method => :post } do |inner|
+ expected = { :conditions => { :method => :post, :domain => "www" } }
+ assert_equal expected, inner.method_with_options
+ end
+ end
+ end
+
+ def test_nested_method_with_options_containing_hashes_going_deep
+ with_options :html => { :class => "foo", :style => { :margin => 0, :display => "block" } } do |outer|
+ outer.with_options :html => { :title => "bar", :style => { :margin => "1em", :color => "#fff" } } do |inner|
+ expected = { :html => { :class => "foo", :title => "bar", :style => { :margin => "1em", :display => "block", :color => "#fff" } } }
+ assert_equal expected, inner.method_with_options
+ end
+ end
+ end
+
# Needed when counting objects with the ObjectSpace
def test_option_merger_class_method
assert_equal ActiveSupport::OptionMerger, ActiveSupport::OptionMerger.new('', '').class
diff --git a/railties/config.ru b/railties/config.ru
new file mode 100644
index 0000000000..43492a2dcc
--- /dev/null
+++ b/railties/config.ru
@@ -0,0 +1,17 @@
+# Rackup Configuration
+#
+# Start Rails mongrel server with rackup
+# $ rackup -p 3000 config.ru
+#
+# Start server with webrick (or any compatible Rack server) instead
+# $ rackup -p 3000 -s webrick config.ru
+
+# Require your environment file to bootstrap Rails
+require File.dirname(__FILE__) + '/config/environment'
+
+# Static server middleware
+# You can remove this extra check if you use an asset server
+use Rails::Rack::Static
+
+# Dispatch the request
+run ActionController::Dispatcher.new
diff --git a/railties/doc/guides/actionview/helpers.markdown b/railties/doc/guides/actionview/helpers.markdown
new file mode 100644
index 0000000000..c702e83ff9
--- /dev/null
+++ b/railties/doc/guides/actionview/helpers.markdown
@@ -0,0 +1,91 @@
+Helpers
+====================
+
+Helper Basics
+------------------------
+
+Helpers allow you to encapsulate rendering tasks as reusable functions. Helpers are modules, not classes, so their methods execute in the context in which they are called. They get included in a controller (typically the ApplicationController) using the helper function, like so
+
+ Class ApplicationController < ActionController::Base
+ …
+ helper :menu
+
+ def …
+ end
+ end
+
+In this way, methods in the menu helper are made available to any view or partial in your application. These methods can accept parameters, for example controller instance variables (eg; records or record collections gathered by you current controller), items from the view or partial’s locals[] hash or items from the params[] hash. You may wish to pass your controller instance variables and items from the params[] hash to the locals hash before rendering (See the section on partials). Helper methods can also accept an executable block of code.
+
+It is important to remember, though, that helpers are for rendering, and that they become available once a controller method has returned, while Rails is engaged in rendering the contents generated by a controller method. This means that helper methods are not available from within the methods of your controllers.
+
+Helpers can accomplish a variety of tasks, from formatting a complex tag for embedding content for a browser plugin (eg; Flash), to assembling a menu of options appropriate for the current context of your application, to generating sections of forms that get assembled on-the-fly.
+
+Helpers are organized around rendering tasks, so it is not necessary (nor necessarily desirable) to organize them around your application’s controllers or models. In fact, one of the benefits of helpers is that they are not connected via a rendering pipeline to specific controllers, like views and partials are. They can and should handle more generalized tasks.
+
+Here is a very simple, pseudo-example:
+
+ module MenuHelper
+ def menu(records, menu_options={})
+ item_options = menu_options.merge({<some stuff>})
+ items = records.collect |record| do
+ menu_item(record, options)
+ end
+ content_tag(“ul”, items, options)
+ end
+
+ def menu_item(record, item_options={}))
+ action = item_options[:action]
+ action ||= “show”
+ content_tag(“li”, link_to(record.title, :action => action, item_options)
+ end
+ end
+
+
+This helper will require that records passed into it have certain fields (notably :title). The helper could be written to use this as a default, allowing the field to be overwritten by an element of item_options.
+
+Look at the Rails API for examples of helpers included in Rails, eg; [`ActionView::Helpers::ActiveRecordHelper`](http://api.rubyonrails.org/classes/ActionView/Helpers/ActiveRecordHelper.html).
+
+Passing Blocks to Helper Methods
+------------------------
+
+We mentioned before that blocks can be passed to helper methods. This allows for an interesting wrinkle: a block passed to a helper method can cause it to render a partial, which can then be wrapped by the helper method’s output. This can make your helper method much more reusable. It doesn’t need to know anything about the internals about what it is rendering, it just contextualizes it for the page. You can also use the helper to modify the locals hash for the partial, based on some configuration information unique to the current controller. You could implement a flexible themes system in this way.
+
+
+Partials vs. Helpers?
+------------------------
+
+In general, the choice between using a partial vs. using a helper depends on the amount of flexibility you need. If the task is more about reacting to conditions than performing actual rendering, you may likely want a helper method. If you want to be able to call it from a variety of views, again, you may want to use a helper method. You can expect to extract helper methods out of code in views and partials during refactoring.
+
+
+Tutorial -- Calling a Helper [UNFINISHED]
+------------------------
+
+1. Create a Rails application using `rails helper_test`
+Notice the code:
+
+ class ApplicationController < ActionController::Base
+ helper :all # include all helpers, all the time
+For this tutorial, we'll keep this code, but you will likely want to exert more control over loading your helpers.
+
+2. Configure a database of your choice for the app.
+
+3. Inside of the `/app/helpers/` directory, create a new file called, `menu_helper.rb`. Write this in the file:
+
+ module MenuHelpers
+ def menu(records, item_proc=nil)
+ items = records.collect{ |record|
+ menu_item(record, item_proc)
+ }
+ content_tag("ul", items)
+ end
+
+ def menu_item(record, item_proc=nil)
+ item_url = item_proc.call(record)
+ item_url ||= { :action => :show }
+ content_tag("li", link_to(record.name, item_url))
+ end
+ end
+
+4. Create a scaffold for some object in your app, using `./script/generate scaffold widgets`.
+5. Create a database table for your widgets, with at least the fields `name` and `id`. Create a few widgets.
+6. Call the menu command twice from `index.html.erb`, once using the default action, and once supplying a Proc to generate urls. \ No newline at end of file
diff --git a/railties/doc/guides/actionview/partials.markdown b/railties/doc/guides/actionview/partials.markdown
new file mode 100644
index 0000000000..2988b933bc
--- /dev/null
+++ b/railties/doc/guides/actionview/partials.markdown
@@ -0,0 +1,90 @@
+A Guide to Using Partials
+===============================
+
+This guide elaborates on the use and function of partials in Ruby on Rails. As your Rails application grows, your view templates can start to contain a lot of duplicate view code. To manage and reduce this complexity, you can by abstract view template code into partials. Partials are reusable snippets of eRB template code stored in separate files with an underscore ('_') prefix.
+
+Partials can be located anywhere in the `app/views` directory. File extensions for partials work just like other template files, they bear an extension that denotes what kind of code they generate. For example, `_animal.html.erb` and `_animal.xml.erb` are valid filenames for partials.
+
+Partials can be inserted in eRB template code by calling the `render` method with the `:partial` option. For example:
+
+ <%= render :partial => 'foo' %>
+
+This inserts the result of evaluating the template `_foo.html.erb` into the parent template file at this location. Note that `render` assumes that the partial will be in the same directory as the calling parent template and have the same file extension. Partials can be located anywhere within the `app/views` directory. To use a partial located in a different directory then it the parent, add a '/' before it:
+
+ <%= render :partial => '/common/foo' %>
+
+Loads the partial file from the `app/views/common/_foo.html.erb` directory.
+
+Abstracting views into partials can be approached in a number of different ways, depending on the situation. Sometimes, the code that you are abstracting is a specialized view of an object or a collection of objects. Other times, you can look at partials as a reusable subroutine. We'll explore each of these approaches and when to use them as well as the syntax for calling them.
+
+Partials as a View Subroutine
+-----------------------------
+
+Using the `:locals` option, you can pass a hash of values which will be treated as local variables within the partial template.
+
+ <%= render :partial => "person", :locals => { :name => "david" } %>
+
+The variable `name` contains the value `"david"` within the `_person.html.erb` template. Passing variables in this way allows you to create modular, reusable template files. Note that if you want to make local variables that are optional in some use cases, you will have to set them to a sentinel value such as `nil` when they have not been passed. So, in the example above, if the `name` variable is optional in some use cases, you must set:
+
+ <% name ||= nil -%>
+
+So that you can later check:
+
+ <% if name -%>
+ <p>Hello, <%= name %>!</p>
+ <% end -%>
+
+Otherwise, the if statement will throw an error at runtime.
+
+Another thing to be aware of is that instance variables that are visible to the parent view template are visible to the partial. So you might be tempted to do this:
+
+ <%= render :partial => "person" %>
+
+And then within the partial:
+
+ <% if @name -%>
+ <p>Hello, <%= @name %>!</p>
+ <% end -%>
+
+The potential snag here is that if multiple templates start to rely on this partial, you will need to maintain an instance variable with the same name across all of these templates and controllers. This approach can quickly become brittle if overused.
+
+Partials as a View of an Object
+--------------------------------
+
+Another way to look at partials is to view them as mini-views of a particular object or instance variable. Use the `:object` option to pass an object assigns that object to an instance variable named after the partial itself. For example:
+
+ # Renders the partial, making @new_person available through
+ # the local variable 'person'
+ render :partial => "person", :object => @new_person
+
+If the instance variable `name` in the parent template matches the name of the partial, you can use a shortcut:
+
+ render :partial => "person"
+
+Now the value that was in `@person` in the parent template is accessible as `person` in the partial.
+
+Partials as a View of a Collection
+-----------------------------------
+
+Often it is the case that you need to display not just a single object, but a collection of objects. Rather than having to constantly nest the same partial within the same iterator code, Rails provides a syntactical shortcut using the `:collection` option:
+
+ # Renders a collection of the same partial by making each element
+ # of @winners available through the local variable "person" as it
+ # builds the complete response.
+ render :partial => "person", :collection => @winners
+
+This calls the `_person.html.erb` partial for each person in the `@winners` collection. This also creates a local variable within the partial called `partial_counter` which contains the index of the current value. So for example:
+
+ <%= partial_counter %>) <%= person -%>
+
+Where `@winners` contains three people, produces the following output:
+
+ 1) Bill
+ 2) Jeff
+ 3) Nick
+
+One last detail, you can place an arbitrary snippet of code in between the objects using the `:spacer_template` option.
+
+ # Renders the same collection of partials, but also renders the
+ # person_divider partial between each person partial.
+ render :partial => "person", :collection => @winners, :spacer_template => "person_divider"
diff --git a/railties/doc/guides/activerecord/basics.markdown b/railties/doc/guides/activerecord/basics.markdown
new file mode 100644
index 0000000000..0d030fabf9
--- /dev/null
+++ b/railties/doc/guides/activerecord/basics.markdown
@@ -0,0 +1,56 @@
+Active Record Basics
+====================
+
+
+
+The ActiveRecord Pattern
+------------------------
+
+Active Record (the library) conforms to the active record design pattern. The active record pattern is a design pattern often found in applications that use relational database. The name comes from by Martin Fowler's book *Patterns of Enterprise Application Architecture*, in which he describes an active record object as:
+
+> An object that wraps a row in a database table or view, encapsulates the database access, and adds domain logic on that data.
+
+So, an object that follows the active record pattern encapsulates both data and behavior; in other words, they are responsible for saving and loading to the database and also for any domain logic that acts on the data. The data structure of the Active Record should exactly match that of the database: one field in the class for each column in the table.
+
+The Active Record class typically has methods that do the following:
+
+* Construct an instances of an Active Record class from a SQL result
+* Construct a new class instance for insertion into the table
+* Get and set column values
+* Wrap business logic where appropriate
+* Update existing objects and update the related rows in the database
+
+Mapping Your Database
+---------------------
+
+### Plural tables, singular classes ###
+
+### Schema lives in the database ###
+
+Creating Records
+----------------
+
+### Using save ###
+
+### Using create ###
+
+Retrieving Existing Rows
+------------------------
+
+### Using find ###
+
+### Using find_by_* ###
+
+Editing and Updating Rows
+-------------------------
+
+### Editing an instance
+
+### Using update_all/update_attributes ###
+
+Deleting Data
+-------------
+
+### Destroying a record ###
+
+### Deleting a record ### \ No newline at end of file
diff --git a/railties/doc/guides/creating_plugins/basics.markdown b/railties/doc/guides/creating_plugins/basics.markdown
new file mode 100644
index 0000000000..f59e8728d7
--- /dev/null
+++ b/railties/doc/guides/creating_plugins/basics.markdown
@@ -0,0 +1,861 @@
+Creating Plugin Basics
+====================
+
+Pretend for a moment that you are an avid bird watcher. Your favorite bird is the Yaffle, and you want to create a plugin that allows other developers to share in the Yaffle goodness.
+
+In this tutorial you will learn how to create a plugin that includes:
+
+Core Extensions - extending String:
+
+ # Anywhere
+ "hello".squawk # => "squawk! hello! squawk!"
+
+An `acts_as_yaffle` method for Active Record models that adds a "squawk" method:
+
+ class Hickwall < ActiveRecord::Base
+ acts_as_yaffle :yaffle_text_field => :last_sang_at
+ end
+
+ Hickwall.new.squawk("Hello World")
+
+A view helper that will print out squawking info:
+
+ squawk_info_for(@hickwall)
+
+A generator that creates a migration to add squawk columns to a model:
+
+ script/generate yaffle hickwall
+
+A custom generator command:
+
+ class YaffleGenerator < Rails::Generator::NamedBase
+ def manifest
+ m.yaffle_definition
+ end
+ end
+ end
+
+A custom route method:
+
+ ActionController::Routing::Routes.draw do |map|
+ map.yaffles
+ end
+
+In addition you'll learn how to:
+
+* test your plugins
+* work with init.rb, how to store model, views, controllers, helpers and even other plugins in your plugins
+* create documentation for your plugin.
+* write custom rake tasks in your plugin
+
+Create the basic app
+---------------------
+
+In this tutorial we will create a basic rails application with 1 resource: bird. Start out by building the basic rails app:
+
+> The following instructions will work for sqlite3. For more detailed instructions on how to create a rails app for other databases see the API docs.
+
+ rails plugin_demo
+ cd plugin_demo
+ script/generate scaffold bird name:string
+ rake db:migrate
+ script/server
+
+Then navigate to [http://localhost:3000/birds](http://localhost:3000/birds). Make sure you have a functioning rails app before continuing.
+
+Create the plugin
+-----------------------
+
+The built-in Rails plugin generator stubs out a new plugin. Pass the plugin name, either CamelCased or under_scored, as an argument. Pass --with-generator to add an example generator also.
+
+This creates a plugin in vendor/plugins including an init.rb and README as well as standard lib, task, and test directories.
+
+Examples:
+
+ ./script/generate plugin BrowserFilters
+ ./script/generate plugin BrowserFilters --with-generator
+
+Later in the plugin we will create a generator, so go ahead and add the --with-generator option now:
+
+ script/generate plugin yaffle --with-generator
+
+You should see the following output:
+
+ create vendor/plugins/yaffle/lib
+ create vendor/plugins/yaffle/tasks
+ create vendor/plugins/yaffle/test
+ create vendor/plugins/yaffle/README
+ create vendor/plugins/yaffle/MIT-LICENSE
+ create vendor/plugins/yaffle/Rakefile
+ create vendor/plugins/yaffle/init.rb
+ create vendor/plugins/yaffle/install.rb
+ create vendor/plugins/yaffle/uninstall.rb
+ create vendor/plugins/yaffle/lib/yaffle.rb
+ create vendor/plugins/yaffle/tasks/yaffle_tasks.rake
+ create vendor/plugins/yaffle/test/core_ext_test.rb
+ create vendor/plugins/yaffle/generators
+ create vendor/plugins/yaffle/generators/yaffle
+ create vendor/plugins/yaffle/generators/yaffle/templates
+ create vendor/plugins/yaffle/generators/yaffle/yaffle_generator.rb
+ create vendor/plugins/yaffle/generators/yaffle/USAGE
+
+For this plugin you won't need the file vendor/plugins/yaffle/lib/yaffle.rb so you can delete that.
+
+ rm vendor/plugins/yaffle/lib/yaffle.rb
+
+> Editor's note: many plugin authors prefer to keep this file, and add all of the require statements in it. That way, they only line in init.rb would be `require "yaffle"`
+> If you are developing a plugin that has a lot of files in the lib directory, you may want to create a subdirectory like lib/yaffle and store your files in there. That way your init.rb file stays clean
+
+Setup the plugin for testing
+------------------------
+
+Testing plugins that use the entire Rails stack can be complex, and the generator doesn't offer any help. In this tutorial you will learn how to test your plugin against multiple different adapters using ActiveRecord. This tutorial will not cover how to use fixtures in plugin tests.
+
+To setup your plugin to allow for easy testing you'll need to add 3 files:
+
+* A database.yml file with all of your connection strings
+* A schema.rb file with your table definitions
+* A test helper that sets up the database before your tests
+
+For this plugin you'll need 2 tables/models, Hickwalls and Wickwalls, so add the following files:
+
+ # File: vendor/plugins/yaffle/test/database.yml
+
+ sqlite:
+ :adapter: sqlite
+ :dbfile: yaffle_plugin.sqlite.db
+ sqlite3:
+ :adapter: sqlite3
+ :dbfile: yaffle_plugin.sqlite3.db
+ postgresql:
+ :adapter: postgresql
+ :username: postgres
+ :password: postgres
+ :database: yaffle_plugin_test
+ :min_messages: ERROR
+ mysql:
+ :adapter: mysql
+ :host: localhost
+ :username: rails
+ :password:
+ :database: yaffle_plugin_test
+
+ # File: vendor/plugins/yaffle/test/test_helper.rb
+
+ ActiveRecord::Schema.define(:version => 0) do
+ create_table :hickwalls, :force => true do |t|
+ t.string :name
+ t.string :last_squawk
+ t.datetime :last_squawked_at
+ end
+ create_table :wickwalls, :force => true do |t|
+ t.string :name
+ t.string :last_tweet
+ t.datetime :last_tweeted_at
+ end
+ end
+
+ # File: vendor/plugins/yaffle/test/test_helper.rb
+
+ ENV['RAILS_ENV'] = 'test'
+ ENV['RAILS_ROOT'] ||= File.dirname(__FILE__) + '/../../../..'
+
+ require 'test/unit'
+ require File.expand_path(File.join(ENV['RAILS_ROOT'], 'config/environment.rb'))
+
+ config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
+ ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
+
+ db_adapter = ENV['DB']
+
+ # no db passed, try one of these fine config-free DBs before bombing.
+ db_adapter ||=
+ begin
+ require 'rubygems'
+ require 'sqlite'
+ 'sqlite'
+ rescue MissingSourceFile
+ begin
+ require 'sqlite3'
+ 'sqlite3'
+ rescue MissingSourceFile
+ end
+ end
+
+ if db_adapter.nil?
+ raise "No DB Adapter selected. Pass the DB= option to pick one, or install Sqlite or Sqlite3."
+ end
+
+ ActiveRecord::Base.establish_connection(config[db_adapter])
+
+ load(File.dirname(__FILE__) + "/schema.rb")
+
+ require File.dirname(__FILE__) + '/../init.rb'
+
+ class Hickwall < ActiveRecord::Base
+ acts_as_yaffle
+ end
+
+ class Wickwall < ActiveRecord::Base
+ acts_as_yaffle :yaffle_text_field => :last_tweet, :yaffle_date_field => :last_tweeted_at
+ end
+
+Add a `to_squawk` method to String
+-----------------------
+
+To update a core class you will have to:
+
+* Write tests for the desired functionality
+* Create a file for the code you wish to use
+* Require that file from your init.rb
+
+Most plugins store their code classes in the plugin's lib directory. When you add a file to the lib directory, you must also require that file from init.rb. The file you are going to add for this tutorial is `lib/core_ext.rb`
+
+First, you need to write the tests. Testing plugins is very similar to testing rails apps. The generated test file should look something like this:
+
+ # File: vendor/plugins/yaffle/test/core_ext_test.rb
+
+ require 'test/unit'
+
+ class CoreExtTest < Test::Unit::TestCase
+ # Replace this with your real tests.
+ def test_this_plugin
+ flunk
+ end
+ end
+
+Start off by removing the default test, and adding a require statement for your test helper.
+
+ # File: vendor/plugins/yaffle/test/core_ext_test.rb
+
+ require 'test/unit'
+ require File.dirname(__FILE__) + '/test_helper.rb'
+
+ class CoreExtTest < Test::Unit::TestCase
+ end
+
+Navigate to your plugin directory and run `rake test`
+
+ cd vendor/plugins/yaffle
+ rake test
+
+Your test should fail with `no such file to load -- ./test/../lib/core_ext.rb (LoadError)` because we haven't created any file yet. Create the file `lib/core_ext.rb` and re-run the tests. You should see a different error message:
+
+ 1.) Failure ...
+ No tests were specified
+
+Great - now you are ready to start development. The first thing we'll do is to add a method to String called `to_squawk` which will prefix the string with the word "squawk! ". The test will look something like this:
+
+ # File: vendor/plugins/yaffle/init.rb
+
+ class CoreExtTest < Test::Unit::TestCase
+ def test_string_should_respond_to_squawk
+ assert_equal true, "".respond_to?(:to_squawk)
+ end
+ def test_string_prepend_empty_strings_with_the_word_squawk
+ assert_equal "squawk!", "".to_squawk
+ end
+ def test_string_prepend_non_empty_strings_with_the_word_squawk
+ assert_equal "squawk! Hello World", "Hello World".to_squawk
+ end
+ end
+
+ # File: vendor/plugins/yaffle/init.rb
+
+ require "core_ext"
+
+ # File: vendor/plugins/yaffle/lib/core_ext.rb
+
+ String.class_eval do
+ def to_squawk
+ "squawk! #{self}".strip
+ end
+ end
+
+When monkey-patching existing classes it's often better to use `class_eval` instead of opening the class directly.
+
+To test that your method does what it says it does, run the unit tests. To test this manually, fire up a console and start squawking:
+
+ script/console
+ >> "Hello World".to_squawk
+ => "squawk! Hello World"
+
+If that worked, congratulations! You just created your first test-driven plugin that extends a core ruby class.
+
+Add an `acts_as_yaffle` method to ActiveRecord
+-----------------------
+
+A common pattern in plugins is to add a method called `acts_as_something` to models. In this case, you want to write a method called `acts_as_yaffle` that adds a squawk method to your models.
+
+To keep things clean, create a new test file called `acts_as_yaffle_test.rb` in your plugin's test directory and require your test helper.
+
+ # File: vendor/plugins/yaffle/test/acts_as_yaffle_test.rb
+
+ require File.dirname(__FILE__) + '/test_helper.rb'
+
+ class Hickwall < ActiveRecord::Base
+ acts_as_yaffle
+ end
+
+ class ActsAsYaffleTest < Test::Unit::TestCase
+ end
+
+ # File: vendor/plugins/lib/acts_as_yaffle.rb
+
+ module Yaffle
+ end
+
+One of the most common plugin patterns for `acts_as_yaffle` plugins is to structure your file like so:
+
+ module Yaffle
+ def self.included(base)
+ base.send :extend, ClassMethods
+ end
+
+ module ClassMethods
+ # any method placed here will apply to classes, like Hickwall
+ def acts_as_something
+ send :include, InstanceMethods
+ end
+ end
+
+ module InstanceMethods
+ # any method placed here will apply to instaces, like @hickwall
+ end
+ end
+
+With structure you can easily separate the methods that will be used for the class (like `Hickwall.some_method`) and the instance (like `@hickwell.some_method`).
+
+Let's add class method named `acts_as_yaffle` - testing it out first. You already defined the ActiveRecord models in your test helper, so if you run tests now they will fail.
+
+Back in your `acts_as_yaffle` file, update ClassMethods like so:
+
+ module ClassMethods
+ def acts_as_yaffle(options = {})
+ send :include, InstanceMethods
+ end
+ end
+
+Now that test should pass. Since your plugin is going to work with field names, you need to allow people to define the field names, in case there is a naming conflict. You can write a few simple tests for this:
+
+ # File: vendor/plugins/yaffle/test/acts_as_yaffle_test.rb
+
+ require File.dirname(__FILE__) + '/test_helper.rb'
+
+ class ActsAsYaffleTest < Test::Unit::TestCase
+ def test_a_hickwalls_yaffle_text_field_should_be_last_squawk
+ assert_equal "last_squawk", Hickwall.yaffle_text_field
+ end
+ def test_a_hickwalls_yaffle_date_field_should_be_last_squawked_at
+ assert_equal "last_squawked_at", Hickwall.yaffle_date_field
+ end
+ def test_a_wickwalls_yaffle_text_field_should_be_last_tweet
+ assert_equal "last_tweet", Wickwall.yaffle_text_field
+ end
+ def test_a_wickwalls_yaffle_date_field_should_be_last_tweeted_at
+ assert_equal "last_tweeted_at", Wickwall.yaffle_date_field
+ end
+ end
+
+To make these tests pass, you could modify your `acts_as_yaffle` file like so:
+
+ # File: vendor/plugins/yaffle/lib/acts_as_yaffle.rb
+
+ module Yaffle
+ def self.included(base)
+ base.send :extend, ClassMethods
+ end
+
+ module ClassMethods
+ def acts_as_yaffle(options = {})
+ cattr_accessor :yaffle_text_field, :yaffle_date_field
+ self.yaffle_text_field = (options[:yaffle_text_field] || :last_squawk).to_s
+ self.yaffle_date_field = (options[:yaffle_date_field] || :last_squawked_at).to_s
+ send :include, InstanceMethods
+ end
+ end
+
+ module InstanceMethods
+ end
+ end
+
+Now you can add tests for the instance methods, and the instance method itself:
+
+ # File: vendor/plugins/yaffle/test/acts_as_yaffle_test.rb
+
+ require File.dirname(__FILE__) + '/test_helper.rb'
+
+ class ActsAsYaffleTest < Test::Unit::TestCase
+
+ def test_a_hickwalls_yaffle_text_field_should_be_last_squawk
+ assert_equal "last_squawk", Hickwall.yaffle_text_field
+ end
+ def test_a_hickwalls_yaffle_date_field_should_be_last_squawked_at
+ assert_equal "last_squawked_at", Hickwall.yaffle_date_field
+ end
+
+ def test_a_wickwalls_yaffle_text_field_should_be_last_squawk
+ assert_equal "last_tweet", Wickwall.yaffle_text_field
+ end
+ def test_a_wickwalls_yaffle_date_field_should_be_last_squawked_at
+ assert_equal "last_tweeted_at", Wickwall.yaffle_date_field
+ end
+
+ def test_hickwalls_squawk_should_populate_last_squawk
+ hickwall = Hickwall.new
+ hickwall.squawk("Hello World")
+ assert_equal "squawk! Hello World", hickwall.last_squawk
+ end
+ def test_hickwalls_squawk_should_populate_last_squawked_at
+ hickwall = Hickwall.new
+ hickwall.squawk("Hello World")
+ assert_equal Date.today, hickwall.last_squawked_at
+ end
+
+ def test_wickwalls_squawk_should_populate_last_tweet
+ wickwall = Wickwall.new
+ wickwall.squawk("Hello World")
+ assert_equal "squawk! Hello World", wickwall.last_tweet
+ end
+ def test_wickwalls_squawk_should_populate_last_tweeted_at
+ wickwall = Wickwall.new
+ wickwall.squawk("Hello World")
+ assert_equal Date.today, wickwall.last_tweeted_at
+ end
+ end
+
+ # File: vendor/plugins/yaffle/lib/acts_as_yaffle.rb
+
+ module Yaffle
+ def self.included(base)
+ base.send :extend, ClassMethods
+ end
+
+ module ClassMethods
+ def acts_as_yaffle(options = {})
+ cattr_accessor :yaffle_text_field, :yaffle_date_field
+ self.yaffle_text_field = (options[:yaffle_text_field] || :last_squawk).to_s
+ self.yaffle_date_field = (options[:yaffle_date_field] || :last_squawked_at).to_s
+ send :include, InstanceMethods
+ end
+ end
+
+ module InstanceMethods
+ def squawk(string)
+ write_attribute(self.class.yaffle_text_field, string.to_squawk)
+ write_attribute(self.class.yaffle_date_field, Date.today)
+ end
+ end
+ end
+
+Note the use of write_attribute to write to the field in model.
+
+Create a view helper
+-----------------------
+
+Creating a view helper is a 3-step process:
+
+* Add an appropriately named file to the lib directory
+* Require the file and hooks in init.rb
+* Write the tests
+
+First, create the test to define the functionality you want:
+
+ # File: vendor/plugins/yaffle/test/view_helpers_test.rb
+
+ require File.dirname(__FILE__) + '/test_helper.rb'
+ include YaffleViewHelper
+
+ class ViewHelpersTest < Test::Unit::TestCase
+ def test_squawk_info_for_should_return_the_text_and_date
+ time = Time.now
+ hickwall = Hickwall.new
+ hickwall.last_squawk = "Hello World"
+ hickwall.last_squawked_at = time
+ assert_equal "Hello World, #{time.to_s}", squawk_info_for(hickwall)
+ end
+ end
+
+Then add the following statements to init.rb:
+
+ # File: vendor/plugins/yaffle/init.rb
+
+ require "view_helpers"
+ ActionView::Base.send :include, YaffleViewHelper
+
+Then add the view helpers file and
+
+ # File: vendor/plugins/yaffle/lib/view_helpers.rb
+
+ module YaffleViewHelper
+ def squawk_info_for(yaffle)
+ returning "" do |result|
+ result << yaffle.read_attribute(yaffle.class.yaffle_text_field)
+ result << ", "
+ result << yaffle.read_attribute(yaffle.class.yaffle_date_field).to_s
+ end
+ end
+ end
+
+You can also test this in script/console by using the "helper" method:
+
+ script/console
+ >> helper.squawk_info_for(@some_yaffle_instance)
+
+Create a migration generator
+-----------------------
+
+When you created the plugin above, you specified the --with-generator option, so you already have the generator stubs in your plugin.
+
+We'll be relying on the built-in rails generate template for this tutorial. Going into the details of generators is beyond the scope of this tutorial.
+
+Type:
+
+ script/generate
+
+You should see the line:
+
+ Plugins (vendor/plugins): yaffle
+
+When you run `script/generate yaffle` you should see the contents of your USAGE file. For this plugin, the USAGE file looks like this:
+
+ Description:
+ Creates a migration that adds yaffle squawk fields to the given model
+
+ Example:
+ ./script/generate yaffle hickwall
+
+ This will create:
+ db/migrate/TIMESTAMP_add_yaffle_fields_to_hickwall
+
+Now you can add code to your generator:
+
+ # File: vendor/plugins/yaffle/generators/yaffle/yaffle_generator.rb
+
+ class YaffleGenerator < Rails::Generator::NamedBase
+ def manifest
+ record do |m|
+ m.migration_template 'migration:migration.rb', "db/migrate", {:assigns => yaffle_local_assigns,
+ :migration_file_name => "add_yaffle_fields_to_#{custom_file_name}"
+ }
+ end
+ end
+
+ private
+ def custom_file_name
+ custom_name = class_name.underscore.downcase
+ custom_name = custom_name.pluralize if ActiveRecord::Base.pluralize_table_names
+ end
+
+ def yaffle_local_assigns
+ returning(assigns = {}) do
+ assigns[:migration_action] = "add"
+ assigns[:class_name] = "add_yaffle_fields_to_#{custom_file_name}"
+ assigns[:table_name] = custom_file_name
+ assigns[:attributes] = [Rails::Generator::GeneratedAttribute.new("last_squawk", "string")]
+ assigns[:attributes] << Rails::Generator::GeneratedAttribute.new("last_squawked_at", "datetime")
+ end
+ end
+ end
+
+Note that you need to be aware of whether or not table names are pluralized.
+
+This does a few things:
+
+* Reuses the built in rails migration_template method
+* Reuses the built-in rails migration template
+
+When you run the generator like
+
+ script/generate yaffle bird
+
+You will see a new file:
+
+ # File: db/migrate/20080529225649_add_yaffle_fields_to_birds.rb
+
+ class AddYaffleFieldsToBirds < ActiveRecord::Migration
+ def self.up
+ add_column :birds, :last_squawk, :string
+ add_column :birds, :last_squawked_at, :datetime
+ end
+
+ def self.down
+ remove_column :birds, :last_squawked_at
+ remove_column :birds, :last_squawk
+ end
+ end
+
+Add a custom generator command
+------------------------
+
+You may have noticed above that you can used one of the built-in rails migration commands `m.migration_template`. You can create your own commands for these, using the following steps:
+
+1. Add the require and hook statements to init.rb
+2. Create the commands - creating 3 sets, Create, Destroy, List
+3. Add the method to your generator
+
+Working with the internals of generators is beyond the scope of this tutorial, but here is a basic example:
+
+ # File: vendor/plugins/yaffle/init.rb
+
+ require "commands"
+ Rails::Generator::Commands::Create.send :include, Yaffle::Generator::Commands::Create
+ Rails::Generator::Commands::Destroy.send :include, Yaffle::Generator::Commands::Destroy
+ Rails::Generator::Commands::List.send :include, Yaffle::Generator::Commands::List
+
+ # File: vendor/plugins/yaffle/lib/commands.rb
+
+ require 'rails_generator'
+ require 'rails_generator/commands'
+
+ module Yaffle #:nodoc:
+ module Generator #:nodoc:
+ module Commands #:nodoc:
+ module Create
+ def yaffle_definition
+ file("definition.txt", "definition.txt")
+ end
+ end
+
+ module Destroy
+ def yaffle_definition
+ file("definition.txt", "definition.txt")
+ end
+ end
+
+ module List
+ def yaffle_definition
+ file("definition.txt", "definition.txt")
+ end
+ end
+ end
+ end
+ end
+
+ # File: vendor/plugins/yaffle/generators/yaffle/templates/definition.txt
+
+ Yaffle is a bird
+
+ # File: vendor/plugins/yaffle/generators/yaffle/yaffle_generator.rb
+
+ class YaffleGenerator < Rails::Generator::NamedBase
+ def manifest
+ m.yaffle_definition
+ end
+ end
+ end
+
+This example just uses the built-in "file" method, but you could do anything that ruby allows.
+
+Add a Custom Route
+------------------------
+
+Testing routes in plugins can be complex, especially if the controllers are also in the plugin itself. Jamis Buck showed a great example of this in [http://weblog.jamisbuck.org/2006/10/26/monkey-patching-rails-extending-routes-2](http://weblog.jamisbuck.org/2006/10/26/monkey-patching-rails-extending-routes-2)
+
+ # File: vendor/plugins/yaffle/test/routing_test.rb
+
+ require "#{File.dirname(__FILE__)}/test_helper"
+
+ class RoutingTest < Test::Unit::TestCase
+
+ def setup
+ ActionController::Routing::Routes.draw do |map|
+ map.yaffles
+ end
+ end
+
+ def test_yaffles_route
+ assert_recognition :get, "/yaffles", :controller => "yaffles_controller", :action => "index"
+ end
+
+ private
+
+ # yes, I know about assert_recognizes, but it has proven problematic to
+ # use in these tests, since it uses RouteSet#recognize (which actually
+ # tries to instantiate the controller) and because it uses an awkward
+ # parameter order.
+ def assert_recognition(method, path, options)
+ result = ActionController::Routing::Routes.recognize_path(path, :method => method)
+ assert_equal options, result
+ end
+ end
+
+ # File: vendor/plugins/yaffle/init.rb
+
+ require "routing"
+ ActionController::Routing::RouteSet::Mapper.send :include, Yaffle::Routing::MapperExtensions
+
+ # File: vendor/plugins/yaffle/lib/routing.rb
+
+ module Yaffle #:nodoc:
+ module Routing #:nodoc:
+ module MapperExtensions
+ def yaffles
+ @set.add_route("/yaffles", {:controller => "yaffles_controller", :action => "index"})
+ end
+ end
+ end
+ end
+
+ # File: config/routes.rb
+
+ ActionController::Routing::Routes.draw do |map|
+ ...
+ map.yaffles
+ end
+
+You can also see if your routes work by running `rake routes` from your app directory.
+
+Generate RDoc Documentation
+-----------------------
+
+Once your plugin is stable, the tests pass on all database and you are ready to deploy do everyone else a favor and document it! Luckily, writing documentation for your plugin is easy.
+
+The first step is to update the README file with detailed information about how to use your plugin. A few key things to include are:
+
+* Your name
+* How to install
+* How to add the functionality to the app (several examples of common use cases)
+* Warning, gotchas or tips that might help save users time
+
+Once your README is solid, go through and add rdoc comments to all of the methods that developers will use.
+
+Before you generate your documentation, be sure to go through and add nodoc comments to those modules and methods that are not important to your users.
+
+Once your comments are good to go, navigate to your plugin directory and run
+
+ rake rdoc
+
+Work with init.rb
+------------------------
+
+The plugin initializer script init.rb is invoked via `eval` (not require) so it has slightly different behavior.
+
+If you reopen any classes in init.rb itself your changes will potentially be made to the wrong module. There are 2 ways around this:
+
+The first way is to explicitly define the top-level module space for all modules and classes, like ::Hash
+
+ # File: vendor/plugins/yaffle/init.rb
+
+ class ::Hash
+ def is_a_special_hash?
+ true
+ end
+ end
+
+OR you can use `module_eval` or `class_eval`
+
+ # File: vendor/plugins/yaffle/init.rb
+
+ Hash.class_eval do
+ def is_a_special_hash?
+ true
+ end
+ end
+
+Store models, views, helpers, and controllers in your plugins
+------------------------
+
+You can easily store models, views, helpers and controllers in plugins. Just create a folder for each in the lib folder, add them to the load path and remove them from the load once path:
+
+ # File: vendor/plugins/yaffle/init.rb
+
+ %w{ models controllers helpers }.each do |dir|
+ path = File.join(directory, 'lib', dir)
+ $LOAD_PATH << path
+ Dependencies.load_paths << path
+ Dependencies.load_once_paths.delete(path)
+ end
+
+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.
+
+Adding directories to the load once paths allow those changes to picked up as soon as you save the file - without having to restart the web server.
+
+Write custom rake tasks in your plugin
+-------------------------
+
+When you created the plugin with the built-in rails generator, it generated a rake file for you in `vendor/plugins/yaffle/tasks/yaffle.rake`. Any rake task you add here will be available to the app.
+
+Many plugin authors put all of their rake tasks into a common namespace that is the same as the plugin, like so:
+
+ # File: vendor/plugins/yaffle/tasks/yaffle.rake
+
+ namespace :yaffle do
+ desc "Prints out the word 'Yaffle'"
+ task :squawk => :environment do
+ puts "squawk!"
+ end
+ end
+
+When you run `rake -T` from your plugin you will see
+
+ yaffle:squawk "Prints out..."
+
+You can add as many files as you want in the tasks directory, and if they end in .rake Rails will pick them up.
+
+Store plugins in alternate locations
+-------------------------
+
+You can store plugins wherever you want - you just have to add those plugins to the plugins path in environment.rb
+
+Since the plugin is only loaded after the plugin paths are defined, you can't redefine this in your plugins - but it may be good to now.
+
+You can even store plugins inside of other plugins for complete plugin madness!
+
+ config.plugin_paths << File.join(RAILS_ROOT,"vendor","plugins","yaffle","lib","plugins")
+
+Create your own Plugin Loaders and Plugin Locators
+------------------------
+
+If the built-in plugin behavior is inadequate, you can change almost every aspect of the location and loading process. You can write your own plugin locators and plugin loaders, but that's beyond the scope of this tutorial.
+
+Use Custom Plugin Generators
+------------------------
+
+If you are an RSpec fan, you can install the `rspec_plugin_generator`, which will generate the spec folder and database for you.
+
+[http://github.com/pat-maddox/rspec-plugin-generator/tree/master](http://github.com/pat-maddox/rspec-plugin-generator/tree/master)
+
+References
+------------------------
+
+* [http://nubyonrails.com/articles/the-complete-guide-to-rails-plugins-part-i](http://nubyonrails.com/articles/the-complete-guide-to-rails-plugins-part-i)
+* [http://nubyonrails.com/articles/2006/05/09/the-complete-guide-to-rails-plugins-part-ii](http://nubyonrails.com/articles/2006/05/09/the-complete-guide-to-rails-plugins-part-ii)
+* [http://github.com/technoweenie/attachment_fu/tree/master](http://github.com/technoweenie/attachment_fu/tree/master)
+* [http://daddy.platte.name/2007/05/rails-plugins-keep-initrb-thin.html](http://daddy.platte.name/2007/05/rails-plugins-keep-initrb-thin.html)
+
+Appendices
+------------------------
+
+The final plugin should have a directory structure that looks something like this:
+
+ |-- MIT-LICENSE
+ |-- README
+ |-- Rakefile
+ |-- generators
+ | `-- yaffle
+ | |-- USAGE
+ | |-- templates
+ | | `-- definition.txt
+ | `-- yaffle_generator.rb
+ |-- init.rb
+ |-- install.rb
+ |-- lib
+ | |-- acts_as_yaffle.rb
+ | |-- commands.rb
+ | |-- core_ext.rb
+ | |-- routing.rb
+ | `-- view_helpers.rb
+ |-- tasks
+ | `-- yaffle_tasks.rake
+ |-- test
+ | |-- acts_as_yaffle_test.rb
+ | |-- core_ext_test.rb
+ | |-- database.yml
+ | |-- debug.log
+ | |-- routing_test.rb
+ | |-- schema.rb
+ | |-- test_helper.rb
+ | `-- view_helpers_test.rb
+ |-- uninstall.rb
+ `-- yaffle_plugin.sqlite3.db
diff --git a/railties/environments/production.rb b/railties/environments/production.rb
index 69c8b9ecb6..e915e8be73 100644
--- a/railties/environments/production.rb
+++ b/railties/environments/production.rb
@@ -10,7 +10,6 @@ config.cache_classes = true
# Full error reports are disabled and caching is turned on
config.action_controller.consider_all_requests_local = false
config.action_controller.perform_caching = true
-config.action_view.cache_template_loading = true
# Use a different cache store in production
# config.cache_store = :mem_cache_store
diff --git a/railties/lib/commands/process/spawner.rb b/railties/lib/commands/process/spawner.rb
index fd09daa55b..dc0008698a 100644
--- a/railties/lib/commands/process/spawner.rb
+++ b/railties/lib/commands/process/spawner.rb
@@ -66,9 +66,9 @@ class MongrelSpawner < Spawner
"-l #{OPTIONS[:rails_root]}/log/mongrel.log"
# Add prefix functionality to spawner's call to mongrel_rails
- # Digging through monrel's project subversion server, the earliest
+ # Digging through mongrel's project subversion server, the earliest
# Tag that has prefix implemented in the bin/mongrel_rails file
- # is 0.3.15 which also happens to be the earilest tag listed.
+ # is 0.3.15 which also happens to be the earliest tag listed.
# References: http://mongrel.rubyforge.org/svn/tags
if Mongrel::Const::MONGREL_VERSION.to_f >=0.3 && !OPTIONS[:prefix].nil?
cmd = cmd + " --prefix #{OPTIONS[:prefix]}"
diff --git a/railties/lib/initializer.rb b/railties/lib/initializer.rb
index 7808d88d92..b8b071d4c9 100644
--- a/railties/lib/initializer.rb
+++ b/railties/lib/initializer.rb
@@ -98,7 +98,7 @@ module Rails
# Rails::Initializer.run(:set_load_path)
#
# This is useful if you only want the load path initialized, without
- # incuring the overhead of completely loading the entire environment.
+ # incurring the overhead of completely loading the entire environment.
def self.run(command = :process, configuration = Configuration.new)
yield configuration if block_given?
initializer = new configuration
@@ -414,8 +414,11 @@ Run `rake gems:install` to install the missing gems.
# paths have already been set, it is not changed, otherwise it is
# set to use Configuration#view_path.
def initialize_framework_views
- ActionMailer::Base.template_root ||= configuration.view_path if configuration.frameworks.include?(:action_mailer)
- ActionController::Base.view_paths = [configuration.view_path] if configuration.frameworks.include?(:action_controller) && ActionController::Base.view_paths.empty?
+ ActionView::PathSet::Path.eager_load_templates! if configuration.cache_classes
+ view_path = ActionView::PathSet::Path.new(configuration.view_path)
+
+ ActionMailer::Base.template_root ||= view_path if configuration.frameworks.include?(:action_mailer)
+ ActionController::Base.view_paths = view_path if configuration.frameworks.include?(:action_controller) && ActionController::Base.view_paths.empty?
end
# If Action Controller is not one of the loaded frameworks (Configuration#frameworks)
@@ -528,7 +531,7 @@ Run `rake gems:install` to install the missing gems.
# A stub for setting options on ActiveRecord::Base.
attr_accessor :active_record
- # A stub for setting options on ActiveRecord::Base.
+ # A stub for setting options on ActiveResource::Base.
attr_accessor :active_resource
# A stub for setting options on ActiveSupport.
diff --git a/railties/lib/performance_test_help.rb b/railties/lib/performance_test_help.rb
index a5e52a7acb..5148b4ab77 100644
--- a/railties/lib/performance_test_help.rb
+++ b/railties/lib/performance_test_help.rb
@@ -1,6 +1,5 @@
require 'action_controller/performance_test'
ActionController::Base.perform_caching = true
-ActionView::Base.cache_template_loading = true
ActiveSupport::Dependencies.mechanism = :require
Rails.logger.level = ActiveSupport::BufferedLogger::INFO
diff --git a/railties/lib/rails_generator/commands.rb b/railties/lib/rails_generator/commands.rb
index d258aeaa0a..59af7308fe 100644
--- a/railties/lib/rails_generator/commands.rb
+++ b/railties/lib/rails_generator/commands.rb
@@ -57,6 +57,17 @@ module Rails
end
protected
+ def current_migration_number
+ Dir.glob("#{RAILS_ROOT}/#{@migration_directory}/[0-9]*_*.rb").inject(0) do |max, file_path|
+ n = File.basename(file_path).split('_', 2).first.to_i
+ if n > max then n else max end
+ end
+ end
+
+ def next_migration_number
+ current_migration_number + 1
+ end
+
def migration_directory(relative_path)
directory(@migration_directory = relative_path)
end
@@ -70,7 +81,11 @@ module Rails
end
def next_migration_string(padding = 3)
- Time.now.utc.strftime("%Y%m%d%H%M%S")
+ if ActiveRecord::Base.timestamped_migrations
+ Time.now.utc.strftime("%Y%m%d%H%M%S")
+ else
+ "%.#{padding}d" % next_migration_number
+ end
end
def gsub_file(relative_destination, regexp, *args, &block)
diff --git a/railties/lib/rails_generator/generators/applications/app/app_generator.rb b/railties/lib/rails_generator/generators/applications/app/app_generator.rb
index 80e8eabfd3..98fe163455 100644
--- a/railties/lib/rails_generator/generators/applications/app/app_generator.rb
+++ b/railties/lib/rails_generator/generators/applications/app/app_generator.rb
@@ -46,6 +46,7 @@ class AppGenerator < Rails::Generator::Base
# Root
m.file "fresh_rakefile", "Rakefile"
m.file "README", "README"
+ m.file "config.ru", "config.ru"
# Application
m.template "helpers/application.rb", "app/controllers/application.rb", :assigns => { :app_name => @app_name, :app_secret => md5.hexdigest }
diff --git a/railties/lib/rails_generator/generators/components/scaffold/USAGE b/railties/lib/rails_generator/generators/components/scaffold/USAGE
index a0e4baea08..810aea16f1 100644
--- a/railties/lib/rails_generator/generators/components/scaffold/USAGE
+++ b/railties/lib/rails_generator/generators/components/scaffold/USAGE
@@ -1,10 +1,11 @@
Description:
Scaffolds an entire resource, from model and migration to controller and
views, along with a full test suite. The resource is ready to use as a
- starting point for your restful, resource-oriented application.
+ starting point for your RESTful, resource-oriented application.
- Pass the name of the model, either CamelCased or under_scored, as the first
- argument, and an optional list of attribute pairs.
+ Pass the name of the model (in singular form), either CamelCased or
+ under_scored, as the first argument, and an optional list of attribute
+ pairs.
Attribute pairs are column_name:sql_type arguments specifying the
model's attributes. Timestamps are added by default, so you don't have to
@@ -13,13 +14,16 @@ Description:
You don't have to think up every attribute up front, but it helps to
sketch out a few so you can start working with the resource immediately.
- For example, `scaffold post title:string body:text published:boolean`
+ For example, 'scaffold post title:string body:text published:boolean'
gives you a model with those three attributes, a controller that handles
the create/show/update/destroy, forms to create and edit your posts, and
an index that lists them all, as well as a map.resources :posts
declaration in config/routes.rb.
+ If you want to remove all the generated files, run
+ 'script/destroy scaffold ModelName'.
+
Examples:
- `./script/generate scaffold post` # no attributes, view will be anemic
+ `./script/generate scaffold post`
`./script/generate scaffold post title:string body:text published:boolean`
`./script/generate scaffold purchase order_id:integer amount:decimal`
diff --git a/railties/lib/rails_generator/scripts.rb b/railties/lib/rails_generator/scripts.rb
index f857f68de4..9b1a99838a 100644
--- a/railties/lib/rails_generator/scripts.rb
+++ b/railties/lib/rails_generator/scripts.rb
@@ -45,7 +45,7 @@ module Rails
usage = "\nInstalled Generators\n"
Rails::Generator::Base.sources.inject([]) do |mem, source|
# Using an association list instead of a hash to preserve order,
- # for esthetic reasons more than anything else.
+ # for aesthetic reasons more than anything else.
label = source.label.to_s.capitalize
pair = mem.assoc(label)
mem << (pair = [label, []]) if pair.nil?
diff --git a/railties/lib/rails_generator/scripts/destroy.rb b/railties/lib/rails_generator/scripts/destroy.rb
index 4fcbc3e0df..a7c2a14751 100644
--- a/railties/lib/rails_generator/scripts/destroy.rb
+++ b/railties/lib/rails_generator/scripts/destroy.rb
@@ -3,7 +3,7 @@ require File.dirname(__FILE__) + '/../scripts'
module Rails::Generator::Scripts
class Destroy < Base
mandatory_options :command => :destroy
-
+
protected
def usage_message
usage = "\nInstalled Generators\n"
@@ -15,14 +15,13 @@ module Rails::Generator::Scripts
usage << <<end_blurb
-This script will destroy all files created by the corresponding
-script/generate command. For instance, script/destroy migration CreatePost
-will delete the appropriate ###_create_post.rb file in db/migrate, while
-script/destroy scaffold Post will delete the posts controller and
+script/generate command. For instance, 'script/destroy migration CreatePost'
+will delete the appropriate XXX_create_post.rb migration file in db/migrate,
+while 'script/destroy scaffold Post' will delete the posts controller and
views, post model and migration, all associated tests, and the map.resources
:posts line in config/routes.rb.
-
-For instructions on finding new generators, run script/generate
+
+For instructions on finding new generators, run script/generate.
end_blurb
return usage
end
diff --git a/railties/lib/rails_generator/secret_key_generator.rb b/railties/lib/rails_generator/secret_key_generator.rb
index 64fbbb90f8..5ae492312e 100644
--- a/railties/lib/rails_generator/secret_key_generator.rb
+++ b/railties/lib/rails_generator/secret_key_generator.rb
@@ -23,7 +23,7 @@ module Rails
# Generate a random secret key by using the Win32 API. Raises LoadError
# if the current platform cannot make use of the Win32 API. Raises
- # SystemCallError if some other error occured.
+ # SystemCallError if some other error occurred.
def generate_secret_with_win32_api
# Following code is based on David Garamond's GUID library for Ruby.
require 'Win32API'
diff --git a/railties/test/generators/generator_test_helper.rb b/railties/test/generators/generator_test_helper.rb
index 05dca3400e..80d5b145be 100644
--- a/railties/test/generators/generator_test_helper.rb
+++ b/railties/test/generators/generator_test_helper.rb
@@ -226,7 +226,7 @@ class GeneratorTestCase < Test::Unit::TestCase
end
end
- # Asserts that the given fixtures yaml file was generated.
+ # Asserts that the given fixtures YAML file was generated.
# It takes a fixture name without the <tt>.yml</tt> part.
# The parsed YAML tree is passed to a block.
def assert_generated_fixtures_for(name)