aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrick <rick@spacemonkey.local>2008-05-10 17:46:55 -0700
committerrick <rick@spacemonkey.local>2008-05-10 17:46:55 -0700
commitd09a8446d5606a5a0b5c024224b09a1318e9cf4d (patch)
tree199ef3554f731c980ea5726e67e34af4ea057c2e
parentc8451aeeea200043d8a3e6eae9c49def3a154ddb (diff)
parenta7ea06b4ebe252e258f83e7de945b4baa30ec3bc (diff)
downloadrails-d09a8446d5606a5a0b5c024224b09a1318e9cf4d.tar.gz
rails-d09a8446d5606a5a0b5c024224b09a1318e9cf4d.tar.bz2
rails-d09a8446d5606a5a0b5c024224b09a1318e9cf4d.zip
fix merge conflict with actionpack changelog
-rw-r--r--actionmailer/lib/action_mailer/base.rb25
-rw-r--r--actionpack/CHANGELOG3
-rw-r--r--actionpack/lib/action_controller/assertions/selector_assertions.rb26
-rwxr-xr-xactionpack/lib/action_controller/base.rb52
-rw-r--r--actionpack/lib/action_controller/caching.rb3
-rw-r--r--actionpack/lib/action_controller/caching/pages.rb31
-rw-r--r--actionpack/lib/action_controller/polymorphic_routes.rb18
-rw-r--r--actionpack/lib/action_controller/record_identifier.rb14
-rw-r--r--actionpack/lib/action_controller/request_forgery_protection.rb4
-rw-r--r--actionpack/lib/action_controller/resources.rb42
-rw-r--r--actionpack/lib/action_controller/routing.rb35
-rw-r--r--actionpack/lib/action_controller/routing/segments.rb9
-rw-r--r--actionpack/lib/action_controller/session/active_record_store.rb21
-rw-r--r--actionpack/lib/action_controller/url_rewriter.rb14
-rw-r--r--actionpack/lib/action_view/base.rb26
-rwxr-xr-xactionpack/lib/action_view/helpers/date_helper.rb9
-rw-r--r--actionpack/lib/action_view/helpers/form_tag_helper.rb6
-rw-r--r--actionpack/lib/action_view/helpers/prototype_helper.rb42
-rw-r--r--actionpack/lib/action_view/helpers/url_helper.rb28
-rw-r--r--actionpack/lib/action_view/partials.rb4
-rw-r--r--actionpack/test/activerecord/render_partial_with_record_identification_test.rb189
-rw-r--r--actionpack/test/controller/new_render_test.rb9
-rw-r--r--actionpack/test/controller/record_identifier_test.rb36
-rw-r--r--actionpack/test/controller/resources_test.rb30
-rw-r--r--actionpack/test/controller/routing_test.rb12
-rwxr-xr-xactionpack/test/template/date_helper_test.rb32
-rw-r--r--activemodel/lib/active_model/validations/associated.rb2
-rw-r--r--activemodel/lib/active_model/validations/numericality.rb22
-rw-r--r--activerecord/CHANGELOG4
-rwxr-xr-xactiverecord/README24
-rw-r--r--activerecord/lib/active_record/association_preload.rb7
-rw-r--r--[-rwxr-xr-x]activerecord/lib/active_record/associations.rb300
-rw-r--r--activerecord/lib/active_record/associations/association_proxy.rb4
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb6
-rwxr-xr-xactiverecord/lib/active_record/base.rb170
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb8
-rwxr-xr-xactiverecord/lib/active_record/connection_adapters/abstract_adapter.rb4
-rwxr-xr-xactiverecord/lib/active_record/connection_adapters/mysql_adapter.rb24
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb18
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb2
-rwxr-xr-xactiverecord/lib/active_record/fixtures.rb2
-rw-r--r--activerecord/lib/active_record/reflection.rb14
-rwxr-xr-xactiverecord/lib/active_record/validations.rb8
-rw-r--r--activerecord/test/cases/adapter_test.rb19
-rw-r--r--activerecord/test/cases/associations/eager_test.rb4
-rwxr-xr-xactiverecord/test/cases/associations_test.rb6
-rwxr-xr-xactiverecord/test/cases/attribute_methods_test.rb35
-rw-r--r--activerecord/test/cases/finder_test.rb9
-rw-r--r--activerecord/test/cases/migration_test.rb18
-rw-r--r--activerecord/test/cases/schema_dumper_test.rb211
-rw-r--r--activerecord/test/models/developer.rb4
-rw-r--r--activerecord/test/models/post.rb2
-rw-r--r--activeresource/lib/active_resource/base.rb232
-rw-r--r--activesupport/CHANGELOG8
-rw-r--r--activesupport/lib/active_support/core_ext/date/calculations.rb5
-rw-r--r--activesupport/lib/active_support/core_ext/hash/indifferent_access.rb43
-rw-r--r--activesupport/lib/active_support/deprecation.rb14
-rw-r--r--activesupport/lib/active_support/inflector.rb5
-rw-r--r--activesupport/lib/active_support/time_with_zone.rb8
-rw-r--r--activesupport/lib/active_support/values/time_zone.rb8
-rw-r--r--activesupport/test/core_ext/date_ext_test.rb23
-rw-r--r--activesupport/test/core_ext/time_with_zone_test.rb10
-rw-r--r--activesupport/test/deprecation_test.rb10
-rw-r--r--activesupport/test/time_zone_test.rb25
-rw-r--r--railties/CHANGELOG2
-rwxr-xr-xrailties/bin/dbconsole3
-rw-r--r--railties/helpers/application.rb5
-rw-r--r--railties/lib/commands/dbconsole.rb55
-rw-r--r--railties/lib/initializer.rb8
-rw-r--r--railties/lib/rails/plugin.rb16
-rw-r--r--railties/lib/rails_generator/generators/applications/app/app_generator.rb2
-rw-r--r--railties/lib/tasks/databases.rake11
73 files changed, 1356 insertions, 790 deletions
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb
index 48e877a182..a3762bf81b 100644
--- a/actionmailer/lib/action_mailer/base.rb
+++ b/actionmailer/lib/action_mailer/base.rb
@@ -95,7 +95,7 @@ module ActionMailer #:nodoc:
#
# ActionMailer::Base.default_url_options[:host] = "example.com"
#
- # This can also be set as a configuration option in <tt>environment.rb</tt>:
+ # This can also be set as a configuration option in <tt>config/environment.rb</tt>:
#
# config.action_mailer.default_url_options = { :host => "example.com" }
#
@@ -204,22 +204,23 @@ module ActionMailer #:nodoc:
# Can be set to nil for no logging. Compatible with both Ruby's own Logger and Log4r loggers.
#
# * <tt>smtp_settings</tt> - Allows detailed configuration for <tt>:smtp</tt> delivery method:
- # * <tt>:address</tt> Allows you to use a remote mail server. Just change it from its default "localhost" setting.
- # * <tt>:port</tt> On the off chance that your mail server doesn't run on port 25, you can change it.
- # * <tt>:domain</tt> If you need to specify a HELO domain, you can do it here.
- # * <tt>:user_name</tt> If your mail server requires authentication, set the username in this setting.
- # * <tt>:password</tt> If your mail server requires authentication, set the password in this setting.
- # * <tt>:authentication</tt> If your mail server requires authentication, you need to specify the authentication type here.
+ # * <tt>:address</tt> - Allows you to use a remote mail server. Just change it from its default "localhost" setting.
+ # * <tt>:port</tt> - On the off chance that your mail server doesn't run on port 25, you can change it.
+ # * <tt>:domain</tt> - If you need to specify a HELO domain, you can do it here.
+ # * <tt>:user_name</tt> - If your mail server requires authentication, set the username in this setting.
+ # * <tt>:password</tt> - If your mail server requires authentication, set the password in this setting.
+ # * <tt>:authentication</tt> - If your mail server requires authentication, you need to specify the authentication type here.
# This is a symbol and one of <tt>:plain</tt>, <tt>:login</tt>, <tt>:cram_md5</tt>
#
# * <tt>sendmail_settings</tt> - Allows you to override options for the <tt>:sendmail</tt> delivery method
- # * <tt>:location</tt> The location of the sendmail executable, defaults to "/usr/sbin/sendmail"
- # * <tt>:arguments</tt> The command line arguments
- # * <tt>raise_delivery_errors</tt> - whether or not errors should be raised if the email fails to be delivered.
+ # * <tt>:location</tt> - The location of the sendmail executable, defaults to "/usr/sbin/sendmail"
+ # * <tt>:arguments</tt> - The command line arguments
+ #
+ # * <tt>raise_delivery_errors</tt> - Whether or not errors should be raised if the email fails to be delivered.
#
# * <tt>delivery_method</tt> - Defines a delivery method. Possible values are <tt>:smtp</tt> (default), <tt>:sendmail</tt>, and <tt>:test</tt>.
#
- # * <tt>perform_deliveries</tt> - Determines whether deliver_* methods are actually carried out. By default they are,
+ # * <tt>perform_deliveries</tt> - Determines whether <tt>deliver_*</tt> methods are actually carried out. By default they are,
# but this can be turned off to help functional testing.
#
# * <tt>deliveries</tt> - Keeps an array of all the emails sent out through the Action Mailer with <tt>delivery_method :test</tt>. Most useful
@@ -406,7 +407,7 @@ module ActionMailer #:nodoc:
# templating language other than rhtml or rxml are supported.
# To use this, include in your template-language plugin's init
# code or on a per-application basis, this can be invoked from
- # config/environment.rb:
+ # <tt>config/environment.rb</tt>:
#
# ActionMailer::Base.register_template_extension('haml')
def register_template_extension(extension)
diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG
index 87f570d55c..852e783f40 100644
--- a/actionpack/CHANGELOG
+++ b/actionpack/CHANGELOG
@@ -1,6 +1,9 @@
*SVN*
* Change the request forgery protection to go by Content-Type instead of request.format so that you can't bypass it by POSTing to "#{request.uri}.xml" [rick]
+* InstanceTag#default_time_from_options with hash args uses Time.current as default; respects hash settings when time falls in system local spring DST gap [Geoff Buesing]
+
+* select_date defaults to Time.zone.today when config.time_zone is set [Geoff Buesing]
* Fixed that TextHelper#text_field would corrypt when raw HTML was used as the value (mchenryc, Kevin Glowacz) [#80]
diff --git a/actionpack/lib/action_controller/assertions/selector_assertions.rb b/actionpack/lib/action_controller/assertions/selector_assertions.rb
index 272b8f6841..9ef093acfc 100644
--- a/actionpack/lib/action_controller/assertions/selector_assertions.rb
+++ b/actionpack/lib/action_controller/assertions/selector_assertions.rb
@@ -21,11 +21,11 @@ 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
+ # * +assert_select_rjs+ - Assertions on HTML content of RJS update and
# insertion operations.
- # * #assert_select_encoded -- Assertions on HTML encoded inside XML,
+ # * +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.
+ # * +assert_select_email+ - Assertions on the HTML body of an e-mail.
#
# Also see HTML::Selector to learn how to use selectors.
module SelectorAssertions
@@ -136,27 +136,27 @@ module ActionController
# === Equality Tests
#
# The equality test may be one of the following:
- # * <tt>true</tt> -- Assertion is true if at least one element selected.
- # * <tt>false</tt> -- Assertion is true if no element selected.
- # * <tt>String/Regexp</tt> -- Assertion is true if the text value of at least
+ # * <tt>true</tt> - Assertion is true if at least one element selected.
+ # * <tt>false</tt> - Assertion is true if no element selected.
+ # * <tt>String/Regexp</tt> - Assertion is true if the text value of at least
# one element matches the string or regular expression.
- # * <tt>Integer</tt> -- Assertion is true if exactly that number of
+ # * <tt>Integer</tt> - Assertion is true if exactly that number of
# elements are selected.
- # * <tt>Range</tt> -- Assertion is true if the number of selected
+ # * <tt>Range</tt> - Assertion is true if the number of selected
# elements fit the range.
# If no equality test specified, the assertion is true if at least one
# element selected.
#
# To perform more than one equality tests, use a hash with the following keys:
- # * <tt>:text</tt> -- Narrow the selection to elements that have this text
+ # * <tt>:text</tt> - Narrow the selection to elements that have this text
# value (string or regexp).
- # * <tt>:html</tt> -- Narrow the selection to elements that have this HTML
+ # * <tt>:html</tt> - Narrow the selection to elements that have this HTML
# content (string or regexp).
- # * <tt>:count</tt> -- Assertion is true if the number of selected elements
+ # * <tt>:count</tt> - Assertion is true if the number of selected elements
# is equal to this value.
- # * <tt>:minimum</tt> -- Assertion is true if the number of selected
+ # * <tt>:minimum</tt> - Assertion is true if the number of selected
# elements is at least this value.
- # * <tt>:maximum</tt> -- Assertion is true if the number of selected
+ # * <tt>:maximum</tt> - Assertion is true if the number of selected
# elements is at most this value.
#
# If the method is called with a block, once all equality tests are
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index c6d28b492a..e1bf005f39 100755
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -159,28 +159,34 @@ module ActionController #:nodoc:
#
# Hello #{session[:person]}
#
- # For removing objects from the session, you can either assign a single key to nil, like <tt>session[:person] = nil</tt>, or you can
- # remove the entire session with reset_session.
+ # For removing objects from the session, you can either assign a single key to +nil+:
#
- # Sessions are stored in a browser cookie that's cryptographically signed, but unencrypted, by default. This prevents
- # the user from tampering with the session but also allows him to see its contents.
+ # # removes :person from session
+ # session[:person] = nil
#
- # Do not put secret information in session!
+ # or you can remove the entire session with +reset_session+.
+ #
+ # Sessions are stored by default in a browser cookie that's cryptographically signed, but unencrypted.
+ # This prevents the user from tampering with the session but also allows him to see its contents.
+ #
+ # Do not put secret information in cookie-based sessions!
#
# Other options for session storage are:
#
- # ActiveRecordStore: sessions are stored in your database, which works better than PStore with multiple app servers and,
- # unlike CookieStore, hides your session contents from the user. To use ActiveRecordStore, set
+ # * ActiveRecordStore - Sessions are stored in your database, which works better than PStore with multiple app servers and,
+ # unlike CookieStore, hides your session contents from the user. To use ActiveRecordStore, set
#
- # config.action_controller.session_store = :active_record_store
+ # config.action_controller.session_store = :active_record_store
#
- # in your <tt>environment.rb</tt> and run <tt>rake db:sessions:create</tt>.
+ # in your <tt>config/environment.rb</tt> and run <tt>rake db:sessions:create</tt>.
#
- # MemCacheStore: sessions are stored as entries in your memcached cache. Set the session store type in <tt>environment.rb</tt>:
+ # * MemCacheStore - Sessions are stored as entries in your memcached cache.
+ # Set the session store type in <tt>config/environment.rb</tt>:
#
- # config.action_controller.session_store = :mem_cache_store
+ # config.action_controller.session_store = :mem_cache_store
#
- # This assumes that memcached has been installed and configured properly. See the MemCacheStore docs for more information.
+ # This assumes that memcached has been installed and configured properly.
+ # See the MemCacheStore docs for more information.
#
# == Responses
#
@@ -535,20 +541,20 @@ module ActionController #:nodoc:
#
# <tt>url_for</tt> is used to:
#
- # All keys given to url_for are forwarded to the Route module, save for the following:
- # * <tt>:anchor</tt> -- specifies the anchor name to be appended to the path. For example,
+ # All keys given to +url_for+ are forwarded to the Route module, save for the following:
+ # * <tt>:anchor</tt> - Specifies the anchor name to be appended to the path. For example,
# <tt>url_for :controller => 'posts', :action => 'show', :id => 10, :anchor => 'comments'</tt>
# will produce "/posts/show/10#comments".
- # * <tt>:only_path</tt> -- if true, returns the relative URL (omitting the protocol, host name, and port) (<tt>false</tt> by default)
- # * <tt>:trailing_slash</tt> -- if true, adds a trailing slash, as in "/archive/2005/". Note that this
+ # * <tt>:only_path</tt> - If true, returns the relative URL (omitting the protocol, host name, and port) (<tt>false</tt> by default).
+ # * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in "/archive/2005/". Note that this
# is currently not recommended since it breaks caching.
- # * <tt>:host</tt> -- overrides the default (current) host if provided.
- # * <tt>:protocol</tt> -- overrides the default (current) protocol if provided.
- # * <tt>:port</tt> -- optionally specify the port to connect to.
- # * <tt>:user</tt> -- Inline HTTP authentication (only plucked out if <tt>:password</tt> is also present).
- # * <tt>:password</tt> -- Inline HTTP authentication (only plucked out if <tt>:user</tt> is also present).
- # * <tt>:skip_relative_url_root</tt> -- if true, the url is not constructed using the relative_url_root of the request so the path
- # will include the web server relative installation directory.
+ # * <tt>:host</tt> - Overrides the default (current) host if provided.
+ # * <tt>:protocol</tt> - Overrides the default (current) protocol if provided.
+ # * <tt>:port</tt> - Optionally specify the port to connect to.
+ # * <tt>:user</tt> - Inline HTTP authentication (only plucked out if <tt>:password</tt> is also present).
+ # * <tt>:password</tt> - Inline HTTP authentication (only plucked out if <tt>:user</tt> is also present).
+ # * <tt>:skip_relative_url_root</tt> - If true, the url is not constructed using the +relative_url_root+
+ # of the request so the path will include the web server relative installation directory.
#
# The URL is generated from the remaining keys in the hash. A URL contains two key parts: the <base> and a query string.
# Routes composes a query string as the key/value pairs not included in the <base>.
diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb
index 08cbd13876..c4063dfb4b 100644
--- a/actionpack/lib/action_controller/caching.rb
+++ b/actionpack/lib/action_controller/caching.rb
@@ -20,7 +20,8 @@ module ActionController #:nodoc:
#
# == Caching stores
#
- # All the caching stores from ActiveSupport::Cache is available to be used as backends for Action Controller caching.
+ # All the caching stores from ActiveSupport::Cache is available to be used as backends for Action Controller caching. This setting only
+ # affects action and fragment caching as page caching is always written to disk.
#
# Configuration examples (MemoryStore is the default):
#
diff --git a/actionpack/lib/action_controller/caching/pages.rb b/actionpack/lib/action_controller/caching/pages.rb
index 7aa6ce154b..a70ed72f03 100644
--- a/actionpack/lib/action_controller/caching/pages.rb
+++ b/actionpack/lib/action_controller/caching/pages.rb
@@ -4,23 +4,24 @@ require 'uri'
module ActionController #:nodoc:
module Caching
# Page caching is an approach to caching where the entire action output of is stored as a HTML file that the web server
- # can serve without going through the Action Pack. This can be as much as 100 times faster than going through the process of dynamically
- # generating the content. Unfortunately, this incredible speed-up is only available to stateless pages where all visitors
- # are treated the same. Content management systems -- including weblogs and wikis -- have many pages that are a great fit
- # for this approach, but account-based systems where people log in and manipulate their own data are often less likely candidates.
+ # can serve without going through Action Pack. This is the fastest way to cache your content as opposed to going dynamically
+ # through the process of generating the content. Unfortunately, this incredible speed-up is only available to stateless pages
+ # where all visitors are treated the same. Content management systems -- including weblogs and wikis -- have many pages that are
+ # a great fit for this approach, but account-based systems where people log in and manipulate their own data are often less
+ # likely candidates.
#
- # Specifying which actions to cache is done through the <tt>caches</tt> class method:
+ # Specifying which actions to cache is done through the <tt>caches_page</tt> class method:
#
# class WeblogController < ActionController::Base
# caches_page :show, :new
# end
#
- # This will generate cache files such as weblog/show/5 and weblog/new, which match the URLs used to trigger the dynamic
- # generation. This is how the web server is able pick up a cache file when it exists and otherwise let the request pass on to
- # the Action Pack to generate it.
+ # This will generate cache files such as <tt>weblog/show/5.html</tt> and <tt>weblog/new.html</tt>,
+ # which match the URLs used to trigger the dynamic generation. This is how the web server is able
+ # pick up a cache file when it exists and otherwise let the request pass on to Action Pack to generate it.
#
# Expiration of the cache is handled by deleting the cached file, which results in a lazy regeneration approach where the cache
- # is not restored before another hit is made against it. The API for doing so mimics the options from url_for and friends:
+ # is not restored before another hit is made against it. The API for doing so mimics the options from +url_for+ and friends:
#
# class WeblogController < ActionController::Base
# def update
@@ -35,13 +36,17 @@ module ActionController #:nodoc:
#
# == Setting the cache directory
#
- # The cache directory should be the document root for the web server and is set using Base.page_cache_directory = "/document/root".
- # For Rails, this directory has already been set to Rails.public_path (which is usually set to RAILS_ROOT + "/public").
+ # The cache directory should be the document root for the web server and is set using <tt>Base.page_cache_directory = "/document/root"</tt>.
+ # For Rails, this directory has already been set to Rails.public_path (which is usually set to <tt>RAILS_ROOT + "/public"</tt>). Changing
+ # this setting can be useful to avoid naming conflicts with files in <tt>public/</tt>, but doing so will likely require configuring your
+ # web server to look in the new location for cached files.
#
# == Setting the cache extension
#
- # By default, the cache extension is .html, which makes it easy for the cached files to be picked up by the web server. If you want
- # something else, like .php or .shtml, just set Base.page_cache_extension.
+ # Most Rails requests do not have an extension, such as <tt>/weblog/new</tt>. In these cases, the page caching mechanism will add one in
+ # order to make it easy for the cached files to be picked up properly by the web server. By default, this cache extension is <tt>.html</tt>.
+ # If you want something else, like <tt>.php</tt> or <tt>.shtml</tt>, just set Base.page_cache_extension. In cases where a request already has an
+ # extension, such as <tt>.xml</tt> or <tt>.rss</tt>, page caching will not add an extension. This allows it to work well with RESTful apps.
module Pages
def self.included(base) #:nodoc:
base.extend(ClassMethods)
diff --git a/actionpack/lib/action_controller/polymorphic_routes.rb b/actionpack/lib/action_controller/polymorphic_routes.rb
index aa0e05dbd7..e2b7716aa2 100644
--- a/actionpack/lib/action_controller/polymorphic_routes.rb
+++ b/actionpack/lib/action_controller/polymorphic_routes.rb
@@ -47,25 +47,27 @@ module ActionController
# Constructs a call to a named RESTful route for the given record and returns the
# resulting URL string. For example:
#
- # polymorphic_url(post)
- # # calls post_url(post) #=> "http://example.com/posts/1"
+ # # calls post_url(post)
+ # polymorphic_url(post) # => "http://example.com/posts/1"
#
# ==== Options
- # * <tt>:action</tt> -- specifies the action prefix for the named route:
- # <tt>:new</tt>, <tt>:edit</tt> or <tt>:formatted</tt>. Default is no prefix.
- # * <tt>:routing_type</tt> -- <tt>:path</tt> or <tt>:url</tt> (default <tt>:url</tt>).
+ #
+ # * <tt>:action</tt> - Specifies the action prefix for the named route:
+ # <tt>:new</tt>, <tt>:edit</tt>, or <tt>:formatted</tt>. Default is no prefix.
+ # * <tt>:routing_type</tt> - Allowed values are <tt>:path</tt> or <tt>:url</tt>.
+ # Default is <tt>:url</tt>.
#
# ==== Examples
#
# # an Article record
- # polymorphic_url(record) #-> article_url(record)
+ # polymorphic_url(record) # same as article_url(record)
#
# # a Comment record
- # polymorphic_url(record) #-> comment_url(record)
+ # polymorphic_url(record) # same as comment_url(record)
#
# # it recognizes new records and maps to the collection
# record = Comment.new
- # polymorphic_url(record) #-> comments_url()
+ # polymorphic_url(record) # same as comments_url()
#
def polymorphic_url(record_or_hash_or_array, options = {})
if record_or_hash_or_array.kind_of?(Array)
diff --git a/actionpack/lib/action_controller/record_identifier.rb b/actionpack/lib/action_controller/record_identifier.rb
index 22dbc8bbc5..643ff7e5f4 100644
--- a/actionpack/lib/action_controller/record_identifier.rb
+++ b/actionpack/lib/action_controller/record_identifier.rb
@@ -33,11 +33,17 @@ module ActionController
# Returns plural/singular for a record or class. Example:
#
- # partial_path(post) # => "posts/post"
- # partial_path(Person) # => "people/person"
- def partial_path(record_or_class)
+ # partial_path(post) # => "posts/post"
+ # partial_path(Person) # => "people/person"
+ # partial_path(Person, "admin/games") # => "admin/people/person"
+ def partial_path(record_or_class, controller_path = nil)
klass = class_from_record_or_class(record_or_class)
- "#{klass.name.tableize}/#{klass.name.demodulize.underscore}"
+
+ if controller_path && controller_path.include?("/")
+ "#{File.dirname(controller_path)}/#{klass.name.tableize}/#{klass.name.demodulize.underscore}"
+ else
+ "#{klass.name.tableize}/#{klass.name.demodulize.underscore}"
+ end
end
# The DOM class convention is to use the singular form of an object or class. Examples:
diff --git a/actionpack/lib/action_controller/request_forgery_protection.rb b/actionpack/lib/action_controller/request_forgery_protection.rb
index b52a2d4ff1..946a0ed152 100644
--- a/actionpack/lib/action_controller/request_forgery_protection.rb
+++ b/actionpack/lib/action_controller/request_forgery_protection.rb
@@ -69,10 +69,10 @@ module ActionController #:nodoc:
#
# Valid Options:
#
- # * <tt>:only/:except</tt> - passed to the <tt>before_filter</tt> call. Set which actions are verified.
+ # * <tt>:only/:except</tt> - Passed to the <tt>before_filter</tt> call. Set which actions are verified.
# * <tt>:secret</tt> - Custom salt used to generate the <tt>form_authenticity_token</tt>.
# Leave this off if you are using the cookie session store.
- # * <tt>:digest</tt> - Message digest used for hashing. Defaults to 'SHA1'
+ # * <tt>:digest</tt> - Message digest used for hashing. Defaults to 'SHA1'.
def protect_from_forgery(options = {})
self.request_forgery_protection_token ||= :authenticity_token
before_filter :verify_authenticity_token, :only => options.delete(:only), :except => options.delete(:except)
diff --git a/actionpack/lib/action_controller/resources.rb b/actionpack/lib/action_controller/resources.rb
index 0f0fa27d74..26f75780c1 100644
--- a/actionpack/lib/action_controller/resources.rb
+++ b/actionpack/lib/action_controller/resources.rb
@@ -236,27 +236,27 @@ module ActionController
# which takes into account whether <tt>@message</tt> is a new record or not and generates the
# path and method accordingly.
#
- # The #resources method accepts the following options to customize the resulting routes:
- # * <tt>:collection</tt> - add named routes for other actions that operate on the collection.
+ # The +resources+ method accepts the following options to customize the resulting routes:
+ # * <tt>:collection</tt> - Add named routes for other actions that operate on the collection.
# Takes a hash of <tt>#{action} => #{method}</tt>, where method is <tt>:get</tt>/<tt>:post</tt>/<tt>:put</tt>/<tt>:delete</tt>
- # or <tt>:any</tt> if the method does not matter. These routes map to a URL like /messages/rss, with a route of rss_messages_url.
- # * <tt>:member</tt> - same as <tt>:collection</tt>, but for actions that operate on a specific member.
- # * <tt>:new</tt> - same as <tt>:collection</tt>, but for actions that operate on the new resource action.
- # * <tt>:controller</tt> - specify the controller name for the routes.
- # * <tt>:singular</tt> - specify the singular name used in the member routes.
- # * <tt>:requirements</tt> - set custom routing parameter requirements.
- # * <tt>:conditions</tt> - specify custom routing recognition conditions. Resources sets the <tt>:method</tt> value for the method-specific routes.
- # * <tt>:as</tt> - specify a different resource name to use in the URL path. For example:
+ # or <tt>:any</tt> if the method does not matter. These routes map to a URL like /messages/rss, with a route of +rss_messages_url+.
+ # * <tt>:member</tt> - Same as <tt>:collection</tt>, but for actions that operate on a specific member.
+ # * <tt>:new</tt> - Same as <tt>:collection</tt>, but for actions that operate on the new resource action.
+ # * <tt>:controller</tt> - Specify the controller name for the routes.
+ # * <tt>:singular</tt> - Specify the singular name used in the member routes.
+ # * <tt>:requirements</tt> - Set custom routing parameter requirements.
+ # * <tt>:conditions</tt> - Specify custom routing recognition conditions. Resources sets the <tt>:method</tt> value for the method-specific routes.
+ # * <tt>:as</tt> - Specify a different resource name to use in the URL path. For example:
# # products_path == '/productos'
# map.resources :products, :as => 'productos' do |product|
# # product_reviews_path(product) == '/productos/1234/comentarios'
# product.resources :product_reviews, :as => 'comentarios'
# end
#
- # * <tt>:has_one</tt> - specify nested resources, this is a shorthand for mapping singleton resources beneath the current.
- # * <tt>:has_many</tt> - same has <tt>:has_one</tt>, but for plural resources.
+ # * <tt>:has_one</tt> - Specify nested resources, this is a shorthand for mapping singleton resources beneath the current.
+ # * <tt>:has_many</tt> - Same has <tt>:has_one</tt>, but for plural resources.
#
- # You may directly specify the routing association with has_one and has_many like:
+ # You may directly specify the routing association with +has_one+ and +has_many+ like:
#
# map.resources :notes, :has_one => :author, :has_many => [:comments, :attachments]
#
@@ -268,14 +268,14 @@ module ActionController
# notes.resources :attachments
# end
#
- # * <tt>:path_names</tt> - specify different names for the 'new' and 'edit' actions. For example:
+ # * <tt>:path_names</tt> - Specify different names for the 'new' and 'edit' actions. For example:
# # new_products_path == '/productos/nuevo'
# map.resources :products, :as => 'productos', :path_names => { :new => 'nuevo', :edit => 'editar' }
#
# You can also set default action names from an environment, like this:
# config.action_controller.resources_path_names = { :new => 'nuevo', :edit => 'editar' }
#
- # * <tt>:path_prefix</tt> - set a prefix to the routes with required route variables.
+ # * <tt>:path_prefix</tt> - Set a prefix to the routes with required route variables.
#
# Weblog comments usually belong to a post, so you might use resources like:
#
@@ -296,7 +296,7 @@ module ActionController
# article_comments_url(:article_id => @article)
# article_comment_url(:article_id => @article, :id => @comment)
#
- # * <tt>:name_prefix</tt> - define a prefix for all generated routes, usually ending in an underscore.
+ # * <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.
#
# map.resources :tags, :path_prefix => '/books/:book_id', :name_prefix => 'book_'
@@ -343,7 +343,7 @@ module ActionController
# # --> GET /categories/7/messages/1
# # has named route "category_message"
#
- # The #resources method sets HTTP method restrictions on the routes it generates. For example, making an
+ # The +resources+ method sets HTTP method restrictions on the routes it generates. For example, making an
# HTTP POST on <tt>new_message_url</tt> will raise a RoutingError exception. The default route in
# <tt>config/routes.rb</tt> overrides this and allows invalid HTTP methods for resource routes.
def resources(*entities, &block)
@@ -524,11 +524,9 @@ module ActionController
resource.member_methods.each do |method, actions|
actions.each do |action|
action_options = action_options_for(action, resource, method)
- action_path = action
- if resource.options[:path_names]
- action_path = resource.options[:path_names][action]
- action_path ||= Base.resources_path_names[action] || action
- end
+
+ action_path = resource.options[:path_names][action] if resource.options[:path_names].is_a?(Hash)
+ action_path ||= Base.resources_path_names[action] || action
map.named_route("#{action}_#{resource.name_prefix}#{resource.singular}", "#{resource.member_path}#{resource.action_separator}#{action_path}", action_options)
map.named_route("formatted_#{action}_#{resource.name_prefix}#{resource.singular}", "#{resource.member_path}#{resource.action_separator}#{action_path}.:format",action_options)
diff --git a/actionpack/lib/action_controller/routing.rb b/actionpack/lib/action_controller/routing.rb
index 0bffe21431..6aa266513d 100644
--- a/actionpack/lib/action_controller/routing.rb
+++ b/actionpack/lib/action_controller/routing.rb
@@ -15,7 +15,7 @@ module ActionController
# The routing module provides URL rewriting in native Ruby. It's a way to
# redirect incoming requests to controllers and actions. This replaces
# mod_rewrite rules. Best of all, Rails' Routing works with any web server.
- # Routes are defined in routes.rb in your RAILS_ROOT/config directory.
+ # Routes are defined in <tt>config/routes.rb</tt>.
#
# Consider the following route, installed by Rails when you generate your
# application:
@@ -53,7 +53,7 @@ module ActionController
# == Route priority
#
# Not all routes are created equally. Routes have priority defined by the
- # order of appearance of the routes in the routes.rb file. The priority goes
+ # order of appearance of the routes in the <tt>config/routes.rb</tt> file. The priority goes
# from top to bottom. The last route in that file is at the lowest priority
# and will be applied last. If no route matches, 404 is returned.
#
@@ -118,7 +118,7 @@ module ActionController
# root_url # => 'http://www.example.com/'
# root_path # => ''
#
- # You can also specify an already-defined named route in your map.root call:
+ # You can also specify an already-defined named route in your <tt>map.root</tt> call:
#
# # In routes.rb
# map.new_session :controller => 'sessions', :action => 'new'
@@ -217,7 +217,7 @@ module ActionController
# ActionController::Routing::Routes.reload
#
# This will clear all named routes and reload routes.rb if the file has been modified from
- # last load. To absolutely force reloading, use +reload!+.
+ # last load. To absolutely force reloading, use <tt>reload!</tt>.
#
# == Testing Routes
#
@@ -277,6 +277,9 @@ module ActionController
end
class << self
+ # Expects an array of controller names as the first argument.
+ # Executes the passed block with only the named controllers named available.
+ # This method is used in internal Rails testing.
def with_controllers(names)
prior_controllers = @possible_controllers
use_controllers! names
@@ -285,6 +288,10 @@ module ActionController
use_controllers! prior_controllers
end
+ # Returns an array of paths, cleaned of double-slashes and relative path references.
+ # * "\\\" and "//" become "\\" or "/".
+ # * "/foo/bar/../config" becomes "/foo/config".
+ # The returned array is sorted by length, descending.
def normalize_paths(paths)
# do the hokey-pokey of path normalization...
paths = paths.collect do |path|
@@ -303,6 +310,7 @@ module ActionController
paths = paths.uniq.sort_by { |path| - path.length }
end
+ # Returns the array of controller names currently available to ActionController::Routing.
def possible_controllers
unless @possible_controllers
@possible_controllers = []
@@ -327,10 +335,28 @@ module ActionController
@possible_controllers
end
+ # Replaces the internal list of controllers available to ActionController::Routing with the passed argument.
+ # ActionController::Routing.use_controllers!([ "posts", "comments", "admin/comments" ])
def use_controllers!(controller_names)
@possible_controllers = controller_names
end
+ # Returns a controller path for a new +controller+ based on a +previous+ controller path.
+ # Handles 4 scenarios:
+ #
+ # * stay in the previous controller:
+ # controller_relative_to( nil, "groups/discussion" ) # => "groups/discussion"
+ #
+ # * stay in the previous namespace:
+ # controller_relative_to( "posts", "groups/discussion" ) # => "groups/posts"
+ #
+ # * forced move to the root namespace:
+ # controller_relative_to( "/posts", "groups/discussion" ) # => "posts"
+ #
+ # * previous namespace is root:
+ # controller_relative_to( "posts", "anything_with_no_slashes" ) # =>"posts"
+ #
+
def controller_relative_to(controller, previous)
if controller.nil? then previous
elsif controller[0] == ?/ then controller[1..-1]
@@ -344,6 +370,7 @@ module ActionController
Routes = RouteSet.new
::Inflector.module_eval do
+ # Ensures that routes are reloaded when Rails inflections are updated.
def inflections_with_route_reloading(&block)
returning(inflections_without_route_reloading(&block)) {
ActionController::Routing::Routes.reload! if block_given?
diff --git a/actionpack/lib/action_controller/routing/segments.rb b/actionpack/lib/action_controller/routing/segments.rb
index 24ea8c7f2d..b142d18b47 100644
--- a/actionpack/lib/action_controller/routing/segments.rb
+++ b/actionpack/lib/action_controller/routing/segments.rb
@@ -244,11 +244,12 @@ module ActionController
end
class PathSegment < DynamicSegment #:nodoc:
- RESERVED_PCHAR = "#{Segment::RESERVED_PCHAR}/"
- UNSAFE_PCHAR = Regexp.new("[^#{URI::REGEXP::PATTERN::UNRESERVED}#{RESERVED_PCHAR}]", false, 'N').freeze
-
def interpolation_chunk(value_code = "#{local_name}")
- "\#{URI.escape(#{value_code}.to_s, ActionController::Routing::PathSegment::UNSAFE_PCHAR)}"
+ "\#{#{value_code}}"
+ end
+
+ def extract_value
+ "#{local_name} = hash[:#{key}] && hash[:#{key}].collect { |path_component| URI.escape(path_component, ActionController::Routing::Segment::UNSAFE_PCHAR) }.to_param #{"|| #{default.inspect}" if default}"
end
def default
diff --git a/actionpack/lib/action_controller/session/active_record_store.rb b/actionpack/lib/action_controller/session/active_record_store.rb
index 379fcd62b6..1e8eb57acb 100644
--- a/actionpack/lib/action_controller/session/active_record_store.rb
+++ b/actionpack/lib/action_controller/session/active_record_store.rb
@@ -13,7 +13,7 @@ class CGI
# A session store backed by an Active Record class. A default class is
- # provided, but any object duck-typing to an Active Record +Session+ class
+ # provided, but any object duck-typing to an Active Record Session class
# with text +session_id+ and +data+ attributes is sufficient.
#
# The default assumes a +sessions+ tables with columns:
@@ -26,13 +26,13 @@ class CGI
# ActionController::SessionOverflowError will be raised.
#
# You may configure the table name, primary key, and data column.
- # For example, at the end of config/environment.rb:
+ # For example, at the end of <tt>config/environment.rb</tt>:
# CGI::Session::ActiveRecordStore::Session.table_name = 'legacy_session_table'
# CGI::Session::ActiveRecordStore::Session.primary_key = 'session_id'
# CGI::Session::ActiveRecordStore::Session.data_column_name = 'legacy_session_data'
- # Note that setting the primary key to the session_id frees you from
- # having a separate id column if you don't want it. However, you must
- # set session.model.id = session.session_id by hand! A before_filter
+ # Note that setting the primary key to the +session_id+ frees you from
+ # having a separate +id+ column if you don't want it. However, you must
+ # set <tt>session.model.id = session.session_id</tt> by hand! A before filter
# on ApplicationController is a good place.
#
# Since the default class is a simple Active Record, you get timestamps
@@ -42,7 +42,7 @@ class CGI
# You may provide your own session class implementation, whether a
# feature-packed Active Record or a bare-metal high-performance SQL
# store, by setting
- # +CGI::Session::ActiveRecordStore.session_class = MySessionClass+
+ # CGI::Session::ActiveRecordStore.session_class = MySessionClass
# You must implement these methods:
# self.find_by_session_id(session_id)
# initialize(hash_of_session_id_and_data)
@@ -154,8 +154,13 @@ class CGI
# The database connection, table name, and session id and data columns
# are configurable class attributes. Marshaling and unmarshaling
# are implemented as class methods that you may override. By default,
- # marshaling data is +ActiveSupport::Base64.encode64(Marshal.dump(data))+ and
- # unmarshaling data is +Marshal.load(ActiveSupport::Base64.decode64(data))+.
+ # marshaling data is
+ #
+ # ActiveSupport::Base64.encode64(Marshal.dump(data))
+ #
+ # and unmarshaling data is
+ #
+ # Marshal.load(ActiveSupport::Base64.decode64(data))
#
# This marshaling behavior is intended to store the widest range of
# binary session data in a +text+ column. For higher performance,
diff --git a/actionpack/lib/action_controller/url_rewriter.rb b/actionpack/lib/action_controller/url_rewriter.rb
index b143806818..3a38f23396 100644
--- a/actionpack/lib/action_controller/url_rewriter.rb
+++ b/actionpack/lib/action_controller/url_rewriter.rb
@@ -29,16 +29,16 @@ module ActionController
# Generate a url based on the options provided, default_url_options and the
# routes defined in routes.rb. The following options are supported:
#
- # * <tt>:only_path</tt> If true, the relative url is returned. Defaults to +false+.
- # * <tt>:protocol</tt> The protocol to connect to. Defaults to 'http'.
- # * <tt>:host</tt> Specifies the host the link should be targetted at.
+ # * <tt>:only_path</tt> - If true, the relative url is returned. Defaults to +false+.
+ # * <tt>:protocol</tt> - The protocol to connect to. Defaults to 'http'.
+ # * <tt>:host</tt> - Specifies the host the link should be targetted at.
# If <tt>:only_path</tt> is false, this option must be
# provided either explicitly, or via +default_url_options+.
- # * <tt>:port</tt> Optionally specify the port to connect to.
- # * <tt>:anchor</tt> An anchor name to be appended to the path.
- # * <tt>:skip_relative_url_root</tt> If true, the url is not constructed using the
+ # * <tt>:port</tt> - Optionally specify the port to connect to.
+ # * <tt>:anchor</tt> - An anchor name to be appended to the path.
+ # * <tt>:skip_relative_url_root</tt> - If true, the url is not constructed using the
# +relative_url_root+ set in ActionController::AbstractRequest.relative_url_root.
- # * <tt>:trailing_slash</tt> If true, adds a trailing slash, as in "/archive/2009/"
+ # * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in "/archive/2009/"
#
# Any other key (<tt>:controller</tt>, <tt>:action</tt>, etc.) given to
# +url_for+ is forwarded to the Routes module.
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb
index a6da81de07..4840b2526d 100644
--- a/actionpack/lib/action_view/base.rb
+++ b/actionpack/lib/action_view/base.rb
@@ -5,9 +5,9 @@ module ActionView #:nodoc:
class MissingTemplate < ActionViewError #:nodoc:
end
- # Action View templates can be written in three ways. If the template file has a +.erb+ (or +.rhtml+) extension then it uses a mixture of ERb
- # (included in Ruby) and HTML. If the template file has a +.builder+ (or +.rxml+) extension then Jim Weirich's Builder::XmlMarkup library is used.
- # If the template file has a +.rjs+ extension then it will use ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.
+ # Action View templates can be written in three ways. If the template file has a <tt>.erb</tt> (or <tt>.rhtml</tt>) extension then it uses a mixture of ERb
+ # (included in Ruby) and HTML. If the template file has a <tt>.builder</tt> (or <tt>.rxml</tt>) extension then Jim Weirich's Builder::XmlMarkup library is used.
+ # If the template file has a <tt>.rjs</tt> extension then it will use ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.
#
# = ERb
#
@@ -24,7 +24,7 @@ module ActionView #:nodoc:
#
# Hi, Mr. <% puts "Frodo" %>
#
- # If you absolutely must write from within a function, you can use the TextHelper#concat
+ # If you absolutely must write from within a function, you can use the TextHelper#concat.
#
# <%- and -%> suppress leading and trailing whitespace, including the trailing newline, and can be used interchangeably with <% and %>.
#
@@ -46,7 +46,7 @@ module ActionView #:nodoc:
# <% @page_title = "A Wonderful Hello" %>
# <%= render "shared/header" %>
#
- # Now the header can pick up on the @page_title variable and use it for outputting a title tag:
+ # Now the header can pick up on the <tt>@page_title</tt> variable and use it for outputting a title tag:
#
# <title><%= @page_title %></title>
#
@@ -56,7 +56,7 @@ module ActionView #:nodoc:
#
# <%= render "shared/header", { :headline => "Welcome", :person => person } %>
#
- # These can now be accessed in shared/header with:
+ # These can now be accessed in <tt>shared/header</tt> with:
#
# Headline: <%= headline %>
# First name: <%= person.first_name %>
@@ -77,13 +77,13 @@ module ActionView #:nodoc:
#
# == Builder
#
- # Builder templates are a more programmatic alternative to ERb. They are especially useful for generating XML content. An +XmlMarkup+ object
- # named +xml+ is automatically made available to templates with a +.builder+ extension.
+ # Builder templates are a more programmatic alternative to ERb. They are especially useful for generating XML content. An XmlMarkup object
+ # named +xml+ is automatically made available to templates with a <tt>.builder</tt> extension.
#
# Here are some basic examples:
#
# xml.em("emphasized") # => <em>emphasized</em>
- # xml.em { xml.b("emph & bold") } # => <em><b>emph &amp; bold</b></em>
+ # xml.em { xml.b("emph & bold") } # => <em><b>emph &amp; bold</b></em>
# xml.a("A Link", "href"=>"http://onestepback.org") # => <a href="http://onestepback.org">A Link</a>
# xml.target("name"=>"compile", "option"=>"fast") # => <target option="fast" name="compile"\>
# # NOTE: order of attributes is not specified.
@@ -130,18 +130,18 @@ module ActionView #:nodoc:
#
# == JavaScriptGenerator
#
- # JavaScriptGenerator templates end in +.rjs+. Unlike conventional templates which are used to
+ # JavaScriptGenerator templates end in <tt>.rjs</tt>. Unlike conventional templates which are used to
# render the results of an action, these templates generate instructions on how to modify an already rendered page. This makes it easy to
# modify multiple elements on your page in one declarative Ajax response. Actions with these templates are called in the background with Ajax
# and make updates to the page where the request originated from.
#
# An instance of the JavaScriptGenerator object named +page+ is automatically made available to your template, which is implicitly wrapped in an ActionView::Helpers::PrototypeHelper#update_page block.
#
- # When an .rjs action is called with +link_to_remote+, the generated JavaScript is automatically evaluated. Example:
+ # When an <tt>.rjs</tt> action is called with +link_to_remote+, the generated JavaScript is automatically evaluated. Example:
#
# link_to_remote :url => {:action => 'delete'}
#
- # The subsequently rendered +delete.rjs+ might look like:
+ # The subsequently rendered <tt>delete.rjs</tt> might look like:
#
# page.replace_html 'sidebar', :partial => 'sidebar'
# page.remove "person-#{@person.id}"
@@ -185,7 +185,7 @@ module ActionView #:nodoc:
attr_internal :request
delegate :request_forgery_protection_token, :template, :params, :session, :cookies, :response, :headers,
- :flash, :logger, :to => :controller
+ :flash, :logger, :action_name, :to => :controller
module CompiledTemplates #:nodoc:
# holds compiled template code
diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb
index cbd390421a..8a9c8044ae 100755
--- a/actionpack/lib/action_view/helpers/date_helper.rb
+++ b/actionpack/lib/action_view/helpers/date_helper.rb
@@ -283,7 +283,7 @@ module ActionView
# # prefixed with 'payday' rather than 'date'
# select_datetime(my_date_time, :prefix => 'payday')
#
- def select_date(date = Date.today, options = {}, html_options = {})
+ def select_date(date = Date.current, options = {}, html_options = {})
options[:order] ||= []
[:year, :month, :day].each { |o| options[:order].push(o) unless options[:order].include?(o) }
@@ -683,12 +683,13 @@ module ActionView
default[:min] ||= default[:minute]
default[:sec] ||= default[:second]
+ time = Time.current
+
[:year, :month, :day, :hour, :min, :sec].each do |key|
- default[key] ||= Time.now.send(key)
+ default[key] ||= time.send(key)
end
- Time.mktime(default[:year], default[:month], default[:day],
- default[:hour], default[:min], default[:sec])
+ Time.utc(default[:year], default[:month], default[:day], default[:hour], default[:min], default[:sec])
end
end
end
diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb
index f37b428b80..922a0662fe 100644
--- a/actionpack/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb
@@ -316,12 +316,12 @@ module ActionView
# Creates a submit button with the text <tt>value</tt> as the caption.
#
# ==== Options
- # * <tt>:confirm => 'question?'</tt> -- This will add a JavaScript confirm
+ # * <tt>:confirm => 'question?'</tt> - This will add a JavaScript confirm
# prompt with the question specified. If the user accepts, the form is
# processed normally, otherwise no action is taken.
- # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
+ # * <tt>:disabled</tt> - If true, the user will not be able to use this input.
# * <tt>:disable_with</tt> - Value of this parameter will be used as the value for a disabled version
- # of the submit button when the form is submitted.
+ # of the submit button when the form is submitted.
# * Any other key creates standard HTML options for the tag.
#
# ==== Examples
diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb
index 908728c0e6..1b12aa8058 100644
--- a/actionpack/lib/action_view/helpers/prototype_helper.rb
+++ b/actionpack/lib/action_view/helpers/prototype_helper.rb
@@ -631,6 +631,27 @@ module ActionView
# render(:update) { |page| page.update_time }
# end
#
+ # Calls to JavaScriptGenerator not matching a helper method below
+ # generate a proxy to the JavaScript Class named by the method called.
+ #
+ # Examples:
+ #
+ # # Generates:
+ # # Foo.init();
+ # update_page do |page|
+ # page.foo.init
+ # end
+ #
+ # # Generates:
+ # # Event.observe('one', 'click', function () {
+ # # $('two').show();
+ # # });
+ # update_page do |page|
+ # page.event.observe('one', 'click') do |p|
+ # p[:two].show
+ # end
+ # end
+ #
# You can also use PrototypeHelper#update_page_tag instead of
# PrototypeHelper#update_page to wrap the generated JavaScript in a
# <script> tag.
@@ -855,12 +876,21 @@ module ActionView
#
# Examples:
#
- # # Generates: Element.replace(my_element, "My content to replace with.")
- # page.call 'Element.replace', 'my_element', "My content to replace with."
- #
- # # Generates: alert('My message!')
- # page.call 'alert', 'My message!'
- #
+ # # Generates: Element.replace(my_element, "My content to replace with.")
+ # page.call 'Element.replace', 'my_element', "My content to replace with."
+ #
+ # # Generates: alert('My message!')
+ # page.call 'alert', 'My message!'
+ #
+ # # Generates:
+ # # my_method(function() {
+ # # $("one").show();
+ # # $("two").hide();
+ # # });
+ # page.call(:my_method) do |p|
+ # p[:one].show
+ # p[:two].hide
+ # end
def call(function, *arguments, &block)
record "#{function}(#{arguments_for_call(arguments, block)})"
end
diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb
index 6d27494213..375ebfcdc5 100644
--- a/actionpack/lib/action_view/helpers/url_helper.rb
+++ b/actionpack/lib/action_view/helpers/url_helper.rb
@@ -19,15 +19,15 @@ module ActionView
# need an unescaped url, pass <tt>:escape => false</tt> in the +options+.
#
# ==== Options
- # * <tt>:anchor</tt> -- specifies the anchor name to be appended to the path.
- # * <tt>:only_path</tt> -- if true, returns the relative URL (omitting the protocol, host name, and port) (<tt>true</tt> by default unless <tt>:host</tt> is specified)
- # * <tt>:trailing_slash</tt> -- if true, adds a trailing slash, as in "/archive/2005/". Note that this
+ # * <tt>:anchor</tt> - Specifies the anchor name to be appended to the path.
+ # * <tt>:only_path</tt> - If true, returns the relative URL (omitting the protocol, host name, and port) (<tt>true</tt> by default unless <tt>:host</tt> is specified).
+ # * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in "/archive/2005/". Note that this
# is currently not recommended since it breaks caching.
- # * <tt>:host</tt> -- overrides the default (current) host if provided
- # * <tt>:protocol</tt> -- overrides the default (current) protocol if provided
- # * <tt>:user</tt> -- Inline HTTP authentication (only plucked out if <tt>:password</tt> is also present)
- # * <tt>:password</tt> -- Inline HTTP authentication (only plucked out if <tt>:user</tt> is also present)
- # * <tt>:escape</tt> -- Determines whether the returned URL will be HTML escaped or not (<tt>true</tt> by default)
+ # * <tt>:host</tt> - Overrides the default (current) host if provided.
+ # * <tt>:protocol</tt> - Overrides the default (current) protocol if provided.
+ # * <tt>:user</tt> - Inline HTTP authentication (only plucked out if <tt>:password</tt> is also present).
+ # * <tt>:password</tt> - Inline HTTP authentication (only plucked out if <tt>:user</tt> is also present).
+ # * <tt>:escape</tt> - Determines whether the returned URL will be HTML escaped or not (<tt>true</tt> by default).
#
# ==== Relying on named routes
#
@@ -91,14 +91,14 @@ module ActionView
# a name, the link itself will become the name.
#
# ==== Options
- # * <tt>:confirm => 'question?'</tt> -- This will add a JavaScript confirm
+ # * <tt>:confirm => 'question?'</tt> - This will add a JavaScript confirm
# prompt with the question specified. If the user accepts, the link is
# processed normally, otherwise no action is taken.
- # * <tt>:popup => true || array of window options</tt> -- This will force the
+ # * <tt>:popup => true || array of window options</tt> - This will force the
# link to open in a popup window. By passing true, a default browser window
# will be opened with the URL. You can also specify an array of options
# that are passed-thru to JavaScripts window.open method.
- # * <tt>:method => symbol of HTTP verb</tt> -- This modifier will dynamically
+ # * <tt>:method => symbol of HTTP verb</tt> - This modifier will dynamically
# create an HTML form and immediately submit the form for processing using
# the HTTP verb specified. Useful for having links perform a POST operation
# in dangerous actions like deleting a record (which search bots can follow
@@ -178,9 +178,9 @@ module ActionView
# The +options+ hash accepts the same options at url_for.
#
# There are a few special +html_options+:
- # * <tt>:method</tt> -- specifies the anchor name to be appended to the path.
- # * <tt>:disabled</tt> -- specifies the anchor name to be appended to the path.
- # * <tt>:confirm</tt> -- This will add a JavaScript confirm
+ # * <tt>:method</tt> - Specifies the anchor name to be appended to the path.
+ # * <tt>:disabled</tt> - Specifies the anchor name to be appended to the path.
+ # * <tt>:confirm</tt> - This will add a JavaScript confirm
# prompt with the question specified. If the user accepts, the link is
# processed normally, otherwise no action is taken.
#
diff --git a/actionpack/lib/action_view/partials.rb b/actionpack/lib/action_view/partials.rb
index a708ecb3fb..6b294be6bd 100644
--- a/actionpack/lib/action_view/partials.rb
+++ b/actionpack/lib/action_view/partials.rb
@@ -119,7 +119,7 @@ module ActionView
""
end
else
- render_partial(ActionController::RecordIdentifier.partial_path(partial_path), partial_path, local_assigns)
+ render_partial(ActionController::RecordIdentifier.partial_path(partial_path, controller.class.controller_path), partial_path, local_assigns)
end
end
@@ -147,7 +147,7 @@ module ActionView
templates = Hash.new
i = 0
collection.map do |element|
- partial_path = ActionController::RecordIdentifier.partial_path(element)
+ partial_path = ActionController::RecordIdentifier.partial_path(element, controller.class.controller_path)
template = templates[partial_path] ||= ActionView::PartialTemplate.new(self, partial_path, nil, local_assigns)
template.counter = i
i += 1
diff --git a/actionpack/test/activerecord/render_partial_with_record_identification_test.rb b/actionpack/test/activerecord/render_partial_with_record_identification_test.rb
index 32b26206c3..ed10e72953 100644
--- a/actionpack/test/activerecord/render_partial_with_record_identification_test.rb
+++ b/actionpack/test/activerecord/render_partial_with_record_identification_test.rb
@@ -1,49 +1,49 @@
require 'active_record_unit'
-class RenderPartialWithRecordIdentificationTest < ActiveRecordTestCase
- fixtures :developers, :projects, :developers_projects, :topics, :replies, :companies, :mascots
+class RenderPartialWithRecordIdentificationController < ActionController::Base
+ def render_with_has_many_and_belongs_to_association
+ @developer = Developer.find(1)
+ render :partial => @developer.projects
+ end
- class RenderPartialWithRecordIdentificationController < ActionController::Base
- def render_with_has_many_and_belongs_to_association
- @developer = Developer.find(1)
- render :partial => @developer.projects
- end
-
- def render_with_has_many_association
- @topic = Topic.find(1)
- render :partial => @topic.replies
- end
-
- def render_with_named_scope
- render :partial => Reply.base
- end
-
- def render_with_has_many_through_association
- @developer = Developer.find(:first)
- render :partial => @developer.topics
- end
-
- def render_with_has_one_association
- @company = Company.find(1)
- render :partial => @company.mascot
- end
-
- def render_with_belongs_to_association
- @reply = Reply.find(1)
- render :partial => @reply.topic
- end
-
- def render_with_record
- @developer = Developer.find(:first)
- render :partial => @developer
- end
-
- def render_with_record_collection
- @developers = Developer.find(:all)
- render :partial => @developers
- end
+ def render_with_has_many_association
+ @topic = Topic.find(1)
+ render :partial => @topic.replies
+ end
+
+ def render_with_named_scope
+ render :partial => Reply.base
+ end
+
+ def render_with_has_many_through_association
+ @developer = Developer.find(:first)
+ render :partial => @developer.topics
+ end
+
+ def render_with_has_one_association
+ @company = Company.find(1)
+ render :partial => @company.mascot
+ end
+
+ def render_with_belongs_to_association
+ @reply = Reply.find(1)
+ render :partial => @reply.topic
end
- RenderPartialWithRecordIdentificationController.view_paths = [ File.dirname(__FILE__) + "/../fixtures/" ]
+
+ def render_with_record
+ @developer = Developer.find(:first)
+ render :partial => @developer
+ end
+
+ def render_with_record_collection
+ @developers = Developer.find(:all)
+ render :partial => @developers
+ end
+end
+RenderPartialWithRecordIdentificationController.view_paths = [ File.dirname(__FILE__) + "/../fixtures/" ]
+
+class RenderPartialWithRecordIdentificationTest < ActiveRecordTestCase
+ fixtures :developers, :projects, :developers_projects, :topics, :replies, :companies, :mascots
def setup
@controller = RenderPartialWithRecordIdentificationController.new
@@ -84,3 +84,108 @@ class RenderPartialWithRecordIdentificationTest < ActiveRecordTestCase
assert_equal mascot.name, @response.body
end
end
+
+class RenderPartialWithRecordIdentificationController < ActionController::Base
+ def render_with_has_many_and_belongs_to_association
+ @developer = Developer.find(1)
+ render :partial => @developer.projects
+ end
+
+ def render_with_has_many_association
+ @topic = Topic.find(1)
+ render :partial => @topic.replies
+ end
+
+ def render_with_has_many_through_association
+ @developer = Developer.find(:first)
+ render :partial => @developer.topics
+ end
+
+ def render_with_belongs_to_association
+ @reply = Reply.find(1)
+ render :partial => @reply.topic
+ end
+
+ def render_with_record
+ @developer = Developer.find(:first)
+ render :partial => @developer
+ end
+
+ def render_with_record_collection
+ @developers = Developer.find(:all)
+ render :partial => @developers
+ end
+end
+RenderPartialWithRecordIdentificationController.view_paths = [ File.dirname(__FILE__) + "/../fixtures/" ]
+
+class Game < Struct.new(:name, :id)
+ def to_param
+ id.to_s
+ end
+end
+
+module Fun
+ class NestedController < ActionController::Base
+ def render_with_record_in_nested_controller
+ render :partial => Game.new("Pong")
+ end
+
+ def render_with_record_collection_in_nested_controller
+ render :partial => [ Game.new("Pong"), Game.new("Tank") ]
+ end
+ end
+ NestedController.view_paths = [ File.dirname(__FILE__) + "/../fixtures/" ]
+
+ module Serious
+ class NestedDeeperController < ActionController::Base
+ def render_with_record_in_deeper_nested_controller
+ render :partial => Game.new("Chess")
+ end
+
+ def render_with_record_collection_in_deeper_nested_controller
+ render :partial => [ Game.new("Chess"), Game.new("Sudoku"), Game.new("Solitaire") ]
+ end
+ end
+ NestedDeeperController.view_paths = [ File.dirname(__FILE__) + "/../fixtures/" ]
+ end
+end
+
+class RenderPartialWithRecordIdentificationAndNestedControllersTest < ActiveRecordTestCase
+ def setup
+ @controller = Fun::NestedController.new
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ super
+ end
+
+ def test_render_with_record_in_nested_controller
+ get :render_with_record_in_nested_controller
+ assert_template 'fun/games/_game'
+ end
+
+ def test_render_with_record_collection_in_nested_controller
+ get :render_with_record_collection_in_nested_controller
+ assert_template 'fun/games/_game'
+ end
+
+end
+
+class RenderPartialWithRecordIdentificationAndNestedDeeperControllersTest < ActiveRecordTestCase
+ def setup
+ @controller = Fun::Serious::NestedDeeperController.new
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ super
+ end
+
+ def test_render_with_record_in_deeper_nested_controller
+ get :render_with_record_in_deeper_nested_controller
+ assert_template 'fun/serious/games/_game'
+ end
+
+ def test_render_with_record_collection_in_deeper_nested_controller
+ get :render_with_record_collection_in_deeper_nested_controller
+ assert_template 'fun/serious/games/_game'
+ end
+
+end \ No newline at end of file
diff --git a/actionpack/test/controller/new_render_test.rb b/actionpack/test/controller/new_render_test.rb
index 8e39057f55..6e2c6d90c6 100644
--- a/actionpack/test/controller/new_render_test.rb
+++ b/actionpack/test/controller/new_render_test.rb
@@ -246,6 +246,10 @@ class NewRenderTestController < ActionController::Base
def accessing_logger_in_template
render :inline => "<%= logger.class %>"
end
+
+ def accessing_action_name_in_template
+ render :inline => "<%= action_name %>"
+ end
def accessing_params_in_template_with_layout
render :layout => nil, :inline => "Hello: <%= params[:name] %>"
@@ -545,6 +549,11 @@ class NewRenderTest < Test::Unit::TestCase
get :accessing_logger_in_template
assert_equal "Logger", @response.body
end
+
+ def test_access_to_action_name_in_view
+ get :accessing_action_name_in_template
+ assert_equal "accessing_action_name_in_template", @response.body
+ end
def test_render_xml
get :render_xml_hello
diff --git a/actionpack/test/controller/record_identifier_test.rb b/actionpack/test/controller/record_identifier_test.rb
index def8613215..12c1eaea69 100644
--- a/actionpack/test/controller/record_identifier_test.rb
+++ b/actionpack/test/controller/record_identifier_test.rb
@@ -57,6 +57,18 @@ class RecordIdentifierTest < Test::Unit::TestCase
assert_equal expected, partial_path(Comment)
end
+ def test_partial_path_with_namespaced_controller_path
+ expected = "admin/#{@plural}/#{@singular}"
+ assert_equal expected, partial_path(@record, "admin/posts")
+ assert_equal expected, partial_path(@klass, "admin/posts")
+ end
+
+ def test_partial_path_with_not_namespaced_controller_path
+ expected = "#{@plural}/#{@singular}"
+ assert_equal expected, partial_path(@record, "posts")
+ assert_equal expected, partial_path(@klass, "posts")
+ end
+
def test_dom_class
assert_equal @singular, dom_class(@record)
end
@@ -100,4 +112,28 @@ class NestedRecordIdentifierTest < RecordIdentifierTest
assert_equal expected, partial_path(@record)
assert_equal expected, partial_path(Comment::Nested)
end
+
+ def test_partial_path_with_namespaced_controller_path
+ expected = "admin/comment/nesteds/nested"
+ assert_equal expected, partial_path(@record, "admin/posts")
+ assert_equal expected, partial_path(@klass, "admin/posts")
+ end
+
+ def test_partial_path_with_deeper_namespaced_controller_path
+ expected = "deeper/admin/comment/nesteds/nested"
+ assert_equal expected, partial_path(@record, "deeper/admin/posts")
+ assert_equal expected, partial_path(@klass, "deeper/admin/posts")
+ end
+
+ def test_partial_path_with_even_deeper_namespaced_controller_path
+ expected = "even/more/deeper/admin/comment/nesteds/nested"
+ assert_equal expected, partial_path(@record, "even/more/deeper/admin/posts")
+ assert_equal expected, partial_path(@klass, "even/more/deeper/admin/posts")
+ end
+
+ def test_partial_path_with_not_namespaced_controller_path
+ expected = "comment/nesteds/nested"
+ assert_equal expected, partial_path(@record, "posts")
+ assert_equal expected, partial_path(@klass, "posts")
+ end
end
diff --git a/actionpack/test/controller/resources_test.rb b/actionpack/test/controller/resources_test.rb
index b138cee29f..0d089d0f23 100644
--- a/actionpack/test/controller/resources_test.rb
+++ b/actionpack/test/controller/resources_test.rb
@@ -226,6 +226,28 @@ class ResourcesTest < Test::Unit::TestCase
end
end
+ def test_member_when_changed_default_restful_actions_and_path_names_not_specified
+ default_path_names = ActionController::Base.resources_path_names
+ ActionController::Base.resources_path_names = {:new => 'nuevo', :edit => 'editar'}
+
+ with_restful_routing :messages do
+ new_options = { :action => 'new', :controller => 'messages' }
+ new_path = "/messages/nuevo"
+ edit_options = { :action => 'edit', :id => '1', :controller => 'messages' }
+ edit_path = "/messages/1/editar"
+
+ assert_restful_routes_for :messages do |options|
+ assert_recognizes(options.merge(new_options), :path => new_path, :method => :get)
+ end
+
+ assert_restful_routes_for :messages do |options|
+ assert_recognizes(options.merge(edit_options), :path => edit_path, :method => :get)
+ end
+ end
+ ensure
+ ActionController::Base.resources_path_names = default_path_names
+ end
+
def test_with_two_member_actions_with_same_method
[:put, :post].each do |method|
with_restful_routing :messages, :member => { :mark => method, :unmark => method } do
@@ -691,11 +713,11 @@ class ResourcesTest < Test::Unit::TestCase
options[:options] ||= {}
options[:options][:controller] = options[:controller] || controller_name.to_s
- new_action = "new"
- edit_action = "edit"
+ new_action = ActionController::Base.resources_path_names[:new] || "new"
+ edit_action = ActionController::Base.resources_path_names[:edit] || "edit"
if options[:path_names]
- new_action = options[:path_names][:new] || "new"
- edit_action = options[:path_names][:edit] || "edit"
+ new_action = options[:path_names][:new] if options[:path_names][:new]
+ edit_action = options[:path_names][:edit] if options[:path_names][:edit]
end
collection_path = "/#{options[:path_prefix]}#{options[:as] || controller_name}"
diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb
index 640afd58f8..b28f7bcdff 100644
--- a/actionpack/test/controller/routing_test.rb
+++ b/actionpack/test/controller/routing_test.rb
@@ -25,7 +25,7 @@ class UriReservedCharactersRoutingTest < Test::Unit::TestCase
ActionController::Routing.use_controllers! ['controller']
@set = ActionController::Routing::RouteSet.new
@set.draw do |map|
- map.connect ':controller/:action/:variable'
+ map.connect ':controller/:action/:variable/*additional'
end
safe, unsafe = %w(: @ & = + $ , ;), %w(^ / ? # [ ])
@@ -36,17 +36,19 @@ class UriReservedCharactersRoutingTest < Test::Unit::TestCase
end
def test_route_generation_escapes_unsafe_path_characters
- assert_equal "/contr#{@segment}oller/act#{@escaped}ion/var#{@escaped}iable",
+ assert_equal "/contr#{@segment}oller/act#{@escaped}ion/var#{@escaped}iable/add#{@escaped}itional-1/add#{@escaped}itional-2",
@set.generate(:controller => "contr#{@segment}oller",
:action => "act#{@segment}ion",
- :variable => "var#{@segment}iable")
+ :variable => "var#{@segment}iable",
+ :additional => ["add#{@segment}itional-1", "add#{@segment}itional-2"])
end
def test_route_recognition_unescapes_path_components
options = { :controller => "controller",
:action => "act#{@segment}ion",
- :variable => "var#{@segment}iable" }
- assert_equal options, @set.recognize_path("/controller/act#{@escaped}ion/var#{@escaped}iable")
+ :variable => "var#{@segment}iable",
+ :additional => ["add#{@segment}itional-1", "add#{@segment}itional-2"] }
+ assert_equal options, @set.recognize_path("/controller/act#{@escaped}ion/var#{@escaped}iable/add#{@escaped}itional-1/add#{@escaped}itional-2")
end
end
diff --git a/actionpack/test/template/date_helper_test.rb b/actionpack/test/template/date_helper_test.rb
index 2373600bfe..ae83c7bf47 100755
--- a/actionpack/test/template/date_helper_test.rb
+++ b/actionpack/test/template/date_helper_test.rb
@@ -950,6 +950,15 @@ class DateHelperTest < ActionView::TestCase
expects(:select_minute).with(time, anything, anything).returns('')
select_time
end
+
+ def test_select_date_uses_date_current_as_default
+ date = stub(:year => 2004, :month => 6, :day => 15)
+ Date.expects(:current).returns date
+ expects(:select_year).with(date, anything, anything).returns('')
+ expects(:select_month).with(date, anything, anything).returns('')
+ expects(:select_day).with(date, anything, anything).returns('')
+ select_date
+ end
end
def test_date_select
@@ -1699,4 +1708,27 @@ class DateHelperTest < ActionView::TestCase
assert_dom_equal expected, datetime_select("post", "updated_at", {}, :class => 'selector')
end
+ uses_mocha 'TestInstanceTagDefaultTimeFromOptions' do
+ def test_instance_tag_default_time_from_options_uses_time_current_as_default_when_hash_passed_as_arg
+ dummy_instance_tag = ActionView::Helpers::InstanceTag.new(1,2,3)
+ Time.expects(:current).returns Time.now
+ dummy_instance_tag.send!(:default_time_from_options, :hour => 2)
+ end
+
+ def test_instance_tag_default_time_from_options_respects_hash_arg_settings_when_time_falls_in_system_local_dst_spring_gap
+ with_env_tz('US/Central') do
+ dummy_instance_tag = ActionView::Helpers::InstanceTag.new(1,2,3)
+ Time.stubs(:now).returns Time.local(2006, 4, 2, 1)
+ assert_equal 2, dummy_instance_tag.send!(:default_time_from_options, :hour => 2).hour
+ end
+ end
+ end
+
+ protected
+ def with_env_tz(new_tz = 'US/Eastern')
+ old_tz, ENV['TZ'] = ENV['TZ'], new_tz
+ yield
+ ensure
+ old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ')
+ end
end
diff --git a/activemodel/lib/active_model/validations/associated.rb b/activemodel/lib/active_model/validations/associated.rb
index ae3e4974bc..b2d78af580 100644
--- a/activemodel/lib/active_model/validations/associated.rb
+++ b/activemodel/lib/active_model/validations/associated.rb
@@ -25,7 +25,7 @@ module ActiveModel
#
# Configuration options:
# * <tt>:message</tt> - A custom error message (default is: "is invalid")
- # * <tt>:on</tt> Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>)
+ # * <tt>:on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>)
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
# occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
# method, proc or string should return or evaluate to a true or false value.
diff --git a/activemodel/lib/active_model/validations/numericality.rb b/activemodel/lib/active_model/validations/numericality.rb
index 8f5c5e8df8..92ca5f4082 100644
--- a/activemodel/lib/active_model/validations/numericality.rb
+++ b/activemodel/lib/active_model/validations/numericality.rb
@@ -15,17 +15,17 @@ module ActiveModel
# end
#
# Configuration options:
- # * <tt>:message</tt> - A custom error message (default is: "is not a number")
- # * <tt>:on</tt> Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>)
- # * <tt>:only_integer</tt> Specifies whether the value has to be an integer, e.g. an integral value (default is +false+)
- # * <tt>:allow_nil</tt> Skip validation if attribute is +nil+ (default is +false+). Notice that for fixnum and float columns empty strings are converted to +nil+
- # * <tt>:greater_than</tt> Specifies the value must be greater than the supplied value
- # * <tt>:greater_than_or_equal_to</tt> Specifies the value must be greater than or equal the supplied value
- # * <tt>:equal_to</tt> Specifies the value must be equal to the supplied value
- # * <tt>:less_than</tt> Specifies the value must be less than the supplied value
- # * <tt>:less_than_or_equal_to</tt> Specifies the value must be less than or equal the supplied value
- # * <tt>:odd</tt> Specifies the value must be an odd number
- # * <tt>:even</tt> Specifies the value must be an even number
+ # * <tt>:message</tt> - A custom error message (default is: "is not a number").
+ # * <tt>:on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>).
+ # * <tt>:only_integer</tt> - Specifies whether the value has to be an integer, e.g. an integral value (default is +false+).
+ # * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+ (default is +false+). Notice that for fixnum and float columns empty strings are converted to +nil+.
+ # * <tt>:greater_than</tt> - Specifies the value must be greater than the supplied value.
+ # * <tt>:greater_than_or_equal_to</tt> - Specifies the value must be greater than or equal the supplied value.
+ # * <tt>:equal_to</tt> - Specifies the value must be equal to the supplied value.
+ # * <tt>:less_than</tt> - Specifies the value must be less than the supplied value.
+ # * <tt>:less_than_or_equal_to</tt> - Specifies the value must be less than or equal the supplied value.
+ # * <tt>:odd</tt> - Specifies the value must be an odd number.
+ # * <tt>:even</tt> - Specifies the value must be an even number.
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
# occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
# method, proc or string should return or evaluate to a true or false value.
diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG
index 04cf72b38c..597b876f22 100644
--- a/activerecord/CHANGELOG
+++ b/activerecord/CHANGELOG
@@ -1,5 +1,9 @@
*SVN*
+* Base#instantiate_time_object: eliminate check for Time.zone, since we can assume this is set if time_zone_aware_attributes is set to true [Geoff Buesing]
+
+* Time zone aware attribute methods use Time.zone.parse instead of #to_time for String arguments, so that offset information in String is respected. Resolves #105. [Scott Fleckenstein, Geoff Buesing]
+
* Added change_table for migrations (Jeff Dean) [#71]. Example:
change_table :videos do |t|
diff --git a/activerecord/README b/activerecord/README
index ff3f55ee8a..d68eb28a64 100755
--- a/activerecord/README
+++ b/activerecord/README
@@ -169,10 +169,10 @@ A short rundown of the major features:
class AddSystemSettings < ActiveRecord::Migration
def self.up
create_table :system_settings do |t|
- t.string :name
- t.string :label
- t.text :value
- t.string :type
+ t.string :name
+ t.string :label
+ t.text :value
+ t.string :type
t.integer :position
end
@@ -289,16 +289,6 @@ Bi-directional associations thanks to the "belongs_to" macro
thirty_seven_signals.firm.nil? # true
-== Examples
-
-Active Record ships with a couple of examples that should give you a good feel for
-operating usage. Be sure to edit the <tt>examples/shared_setup.rb</tt> file for your
-own database before running the examples. Possibly also the table definition SQL in
-the examples themselves.
-
-It's also highly recommended to have a look at the unit tests. Read more in link:files/RUNNING_UNIT_TESTS.html
-
-
== Philosophy
Active Record attempts to provide a coherent wrapper as a solution for the inconvenience that is
@@ -336,7 +326,7 @@ then use:
% [sudo] gem install activerecord-1.10.0.gem
-You can also install Active Record the old-fashion way with the following command:
+You can also install Active Record the old-fashioned way with the following command:
% [sudo] ruby install.rb
@@ -357,5 +347,5 @@ RubyForge page at http://rubyforge.org/projects/activerecord. And as Jim from Ra
remember to update the corresponding unit tests. If fact, I prefer
new feature to be submitted in the form of new unit tests.
-For other information, feel free to ask on the ruby-talk mailing list
-(which is mirrored to comp.lang.ruby) or contact mailto:david@loudthinking.com.
+For other information, feel free to ask on the rubyonrails-talk
+(http://groups.google.com/group/rubyonrails-talk) mailing list.
diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb
index 3e7c787dee..da4ebdef51 100644
--- a/activerecord/lib/active_record/association_preload.rb
+++ b/activerecord/lib/active_record/association_preload.rb
@@ -65,7 +65,13 @@ module ActiveRecord
end
def set_association_single_records(id_to_record_map, reflection_name, associated_records, key)
+ seen_keys = {}
associated_records.each do |associated_record|
+ #this is a has_one or belongs_to: there should only be one record.
+ #Unfortunately we can't (in portable way) ask the database for 'all records where foo_id in (x,y,z), but please
+ # only one row per distinct foo_id' so this where we enforce that
+ next if seen_keys[associated_record[key].to_s]
+ seen_keys[associated_record[key].to_s] = true
mapped_records = id_to_record_map[associated_record[key].to_s]
mapped_records.each do |mapped_record|
mapped_record.send("set_#{reflection_name}_target", associated_record)
@@ -122,7 +128,6 @@ module ActiveRecord
else
records.each {|record| record.send("set_#{reflection.name}_target", nil)}
-
set_association_single_records(id_to_record_map, reflection.name, find_associated_records(ids, reflection, preload_options), reflection.primary_key_name)
end
end
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 7d27b0607a..fb5f1f8a8c 100755..100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -110,7 +110,7 @@ module ActiveRecord
#
# Don't create associations that have the same name as instance methods of ActiveRecord::Base. Since the association
# adds a method with that name to its model, it will override the inherited method and break things.
- # For instance, #attributes and #connection would be bad choices for association names.
+ # For instance, +attributes+ and +connection+ would be bad choices for association names.
#
# == Auto-generated methods
#
@@ -258,7 +258,7 @@ module ActiveRecord
# order to update their primary keys - except if the parent object is unsaved (<tt>new_record? == true</tt>).
# * If either of these saves fail (due to one of the objects being invalid) the assignment statement returns +false+ and the assignment
# is cancelled.
- # * If you wish to assign an object to a +has_one+ association without saving it, use the <tt>#association.build</tt> method (documented below).
+ # * If you wish to assign an object to a +has_one+ association without saving it, use the <tt>association.build</tt> method (documented below).
# * Assigning an object to a +belongs_to+ association does not save the object, since the foreign key field belongs on the parent. It
# does not save the parent either.
#
@@ -266,8 +266,8 @@ module ActiveRecord
#
# * Adding an object to a collection (+has_many+ or +has_and_belongs_to_many+) automatically saves that object, except if the parent object
# (the owner of the collection) is not yet stored in the database.
- # * If saving any of the objects being added to a collection (via <tt>#push</tt> or similar) fails, then <tt>#push</tt> returns +false+.
- # * You can add an object to a collection without automatically saving it by using the <tt>#collection.build</tt> method (documented below).
+ # * If saving any of the objects being added to a collection (via <tt>push</tt> or similar) fails, then <tt>push</tt> returns +false+.
+ # * You can add an object to a collection without automatically saving it by using the <tt>collection.build</tt> method (documented below).
# * All unsaved (<tt>new_record? == true</tt>) members of the collection are automatically saved when the parent is saved.
#
# === Association callbacks
@@ -504,8 +504,8 @@ module ActiveRecord
#
# Address.find(:all, :include => :addressable) # INVALID
#
- # will raise <tt>ActiveRecord::EagerLoadPolymorphicError</tt>. The reason is that the parent model's type
- # is a column value so its corresponding table name cannot be put in the FROM/JOIN clauses of that early query.
+ # will raise ActiveRecord::EagerLoadPolymorphicError. The reason is that the parent model's type
+ # is a column value so its corresponding table name cannot be put in the +FROM+/+JOIN+ clauses of that early query.
#
# In versions greater than 2.0.2 eager loading in polymorphic associations is supported
# thanks to a change in the overall preloading strategy.
@@ -575,7 +575,7 @@ module ActiveRecord
# end
# end
#
- # When <tt>Firm#clients</tt> is called, it will in turn call <tt>MyApplication::Business::Company.find(firm.id)</tt>. If you want to associate
+ # 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:
#
# module MyApplication
@@ -603,28 +603,28 @@ module ActiveRecord
# Adds the following methods for retrieval and query of collections of associated objects:
# +collection+ is replaced with the symbol passed as the first argument, so
# <tt>has_many :clients</tt> would add among others <tt>clients.empty?</tt>.
- # * <tt>collection(force_reload = false)</tt> - returns an array of all the associated objects.
+ # * <tt>collection(force_reload = false)</tt> - Returns an array of all the associated objects.
# An empty array is returned if none are found.
- # * <tt>collection<<(object, ...)</tt> - adds one or more objects to the collection by setting their foreign keys to the collection's primary key.
- # * <tt>collection.delete(object, ...)</tt> - removes one or more objects from the collection by setting their foreign keys to NULL.
+ # * <tt>collection<<(object, ...)</tt> - Adds one or more objects to the collection by setting their foreign keys to the collection's primary key.
+ # * <tt>collection.delete(object, ...)</tt> - Removes one or more objects from the collection by setting their foreign keys to +NULL+.
# This will also destroy the objects if they're declared as +belongs_to+ and dependent on this model.
- # * <tt>collection=objects</tt> - replaces the collections content by deleting and adding objects as appropriate.
- # * <tt>collection_singular_ids</tt> - returns an array of the associated objects' ids
- # * <tt>collection_singular_ids=ids</tt> - replace the collection with the objects identified by the primary keys in +ids+
- # * <tt>collection.clear</tt> - removes every object from the collection. This destroys the associated objects if they
+ # * <tt>collection=objects</tt> - Replaces the collections content by deleting and adding objects as appropriate.
+ # * <tt>collection_singular_ids</tt> - Returns an array of the associated objects' ids
+ # * <tt>collection_singular_ids=ids</tt> - Replace the collection with the objects identified by the primary keys in +ids+
+ # * <tt>collection.clear</tt> - Removes every object from the collection. This destroys the associated objects if they
# are associated with <tt>:dependent => :destroy</tt>, deletes them directly from the database if <tt>:dependent => :delete_all</tt>,
- # otherwise sets their foreign keys to NULL.
- # * <tt>collection.empty?</tt> - returns +true+ if there are no associated objects.
- # * <tt>collection.size</tt> - returns the number of associated objects.
- # * <tt>collection.find</tt> - finds an associated object according to the same rules as Base.find.
- # * <tt>collection.build(attributes = {}, ...)</tt> - returns one or more new objects of the collection type that have been instantiated
+ # otherwise sets their foreign keys to +NULL+.
+ # * <tt>collection.empty?</tt> - Returns +true+ if there are no associated objects.
+ # * <tt>collection.size</tt> - Returns the number of associated objects.
+ # * <tt>collection.find</tt> - Finds an associated object according to the same rules as Base.find.
+ # * <tt>collection.build(attributes = {}, ...)</tt> - Returns one or more new objects of the collection type that have been instantiated
# with +attributes+ and linked to this object through a foreign key, but have not yet been saved. *Note:* This only works if an
# associated object already exists, not if it's +nil+!
- # * <tt>collection.create(attributes = {})</tt> - returns a new object of the collection type that has been instantiated
+ # * <tt>collection.create(attributes = {})</tt> - Returns a new object of the collection type that has been instantiated
# with +attributes+, linked to this object through a foreign key, and that has already been saved (if it passed the validation).
# *Note:* This only works if an associated object already exists, not if it's +nil+!
#
- # Example: A +Firm+ class declares <tt>has_many :clients</tt>, which will add:
+ # Example: A Firm class declares <tt>has_many :clients</tt>, which will add:
# * <tt>Firm#clients</tt> (similar to <tt>Clients.find :all, :conditions => "firm_id = #{id}"</tt>)
# * <tt>Firm#clients<<</tt>
# * <tt>Firm#clients.delete</tt>
@@ -640,45 +640,45 @@ module ActiveRecord
# The declaration can also include an options hash to specialize the behavior of the association.
#
# Options are:
- # * <tt>:class_name</tt> - specify the class name of the association. Use it only if that name can't be inferred
- # from the association name. So <tt>has_many :products</tt> will by default be linked to the +Product+ class, but
- # if the real class name is +SpecialProduct+, you'll have to specify it with this option.
- # * <tt>:conditions</tt> - specify the conditions that the associated objects must meet in order to be included as a +WHERE+
+ # * <tt>:class_name</tt> - Specify the class name of the association. Use it only if that name can't be inferred
+ # from the association name. So <tt>has_many :products</tt> will by default be linked to the Product class, but
+ # if the real class name is SpecialProduct, you'll have to specify it with this option.
+ # * <tt>:conditions</tt> - Specify the conditions that the associated objects must meet in order to be included as a +WHERE+
# SQL fragment, such as <tt>price > 5 AND name LIKE 'B%'</tt>. Record creations from the association are scoped if a hash
# is used. <tt>has_many :posts, :conditions => {:published => true}</tt> will create published posts with <tt>@blog.posts.create</tt>
# or <tt>@blog.posts.build</tt>.
- # * <tt>:order</tt> - specify the order in which the associated objects are returned as an <tt>ORDER BY</tt> SQL fragment,
- # such as <tt>last_name, first_name DESC</tt>
- # * <tt>:foreign_key</tt> - specify the foreign key used for the association. By default this is guessed to be the name
- # of this class in lower-case and +_id+ suffixed. So a +Person+ class that makes a +has_many+ association will use +person_id+
- # as the default +foreign_key+.
- # * <tt>:dependent</tt> - if set to <tt>:destroy</tt> all the associated objects are destroyed
- # alongside this object by calling their destroy method. If set to <tt>:delete_all</tt> all associated
- # objects are deleted *without* calling their destroy method. If set to <tt>:nullify</tt> all associated
- # objects' foreign keys are set to +NULL+ *without* calling their save callbacks. *Warning:* This option is ignored when also using
- # the <tt>through</tt> option.
- # * <tt>:finder_sql</tt> - specify a complete SQL statement to fetch the association. This is a good way to go for complex
+ # * <tt>:order</tt> - Specify the order in which the associated objects are returned as an <tt>ORDER BY</tt> SQL fragment,
+ # such as <tt>last_name, first_name DESC</tt>.
+ # * <tt>:foreign_key</tt> - Specify the foreign key used for the association. By default this is guessed to be the name
+ # of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_many+ association will use "person_id"
+ # as the default <tt>:foreign_key</tt>.
+ # * <tt>:dependent</tt> - If set to <tt>:destroy</tt> all the associated objects are destroyed
+ # alongside this object by calling their +destroy+ method. If set to <tt>:delete_all</tt> all associated
+ # objects are deleted *without* calling their +destroy+ method. If set to <tt>:nullify</tt> all associated
+ # objects' foreign keys are set to +NULL+ *without* calling their +save+ callbacks. *Warning:* This option is ignored when also using
+ # the <tt>:through</tt> option.
+ # * <tt>:finder_sql</tt> - Specify a complete SQL statement to fetch the association. This is a good way to go for complex
# associations that depend on multiple tables. Note: When this option is used, +find_in_collection+ is _not_ added.
- # * <tt>:counter_sql</tt> - specify a complete SQL statement to fetch the size of the association. If <tt>:finder_sql</tt> is
+ # * <tt>:counter_sql</tt> - Specify a complete SQL statement to fetch the size of the association. If <tt>:finder_sql</tt> is
# specified but not <tt>:counter_sql</tt>, <tt>:counter_sql</tt> will be generated by replacing <tt>SELECT ... FROM</tt> with <tt>SELECT COUNT(*) FROM</tt>.
- # * <tt>:extend</tt> - specify a named module for extending the proxy. See "Association extensions".
- # * <tt>:include</tt> - specify second-order associations that should be eager loaded when the collection is loaded.
- # * <tt>:group</tt>: An attribute name by which the result should be grouped. Uses the <tt>GROUP BY</tt> SQL-clause.
- # * <tt>:limit</tt>: An integer determining the limit on the number of rows that should be returned.
- # * <tt>:offset</tt>: An integer determining the offset from where the rows should be fetched. So at 5, it would skip the first 4 rows.
- # * <tt>:select</tt>: By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed if you, for example, want to do a join
+ # * <tt>:extend</tt> - Specify a named module for extending the proxy. See "Association extensions".
+ # * <tt>:include</tt> - Specify second-order associations that should be eager loaded when the collection is loaded.
+ # * <tt>:group</tt> - An attribute name by which the result should be grouped. Uses the <tt>GROUP BY</tt> SQL-clause.
+ # * <tt>:limit</tt> - An integer determining the limit on the number of rows that should be returned.
+ # * <tt>:offset</tt> - An integer determining the offset from where the rows should be fetched. So at 5, it would skip the first 4 rows.
+ # * <tt>:select</tt> - By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed if you, for example, want to do a join
# but not include the joined columns.
- # * <tt>:as</tt>: Specifies a polymorphic interface (See <tt>#belongs_to</tt>).
- # * <tt>:through</tt>: Specifies a Join Model through which to perform the query. Options for <tt>:class_name</tt> and <tt>:foreign_key</tt>
+ # * <tt>:as</tt> - Specifies a polymorphic interface (See <tt>belongs_to</tt>).
+ # * <tt>:through</tt> - Specifies a Join Model through which to perform the query. Options for <tt>:class_name</tt> and <tt>:foreign_key</tt>
# are ignored, as the association uses the source reflection. You can only use a <tt>:through</tt> query through a <tt>belongs_to</tt>
# or <tt>has_many</tt> association on the join model.
- # * <tt>:source</tt>: Specifies the source association name used by <tt>has_many :through</tt> queries. Only use it if the name cannot be
+ # * <tt>:source</tt> - Specifies the source association name used by <tt>has_many :through</tt> queries. Only use it if the name cannot be
# inferred from the association. <tt>has_many :subscribers, :through => :subscriptions</tt> will look for either <tt>:subscribers</tt> or
- # <tt>:subscriber</tt> on +Subscription+, unless a <tt>:source</tt> is given.
- # * <tt>:source_type</tt>: Specifies type of the source association used by <tt>has_many :through</tt> queries where the source
+ # <tt>:subscriber</tt> on Subscription, unless a <tt>:source</tt> is given.
+ # * <tt>:source_type</tt> - Specifies type of the source association used by <tt>has_many :through</tt> queries where the source
# association is a polymorphic +belongs_to+.
- # * <tt>:uniq</tt> - if set to +true+, duplicates will be omitted from the collection. Useful in conjunction with <tt>:through</tt>.
- # * <tt>:readonly</tt> - if set to +true+, all the associated objects are readonly through the association.
+ # * <tt>:uniq</tt> - If true, duplicates will be omitted from the collection. Useful in conjunction with <tt>:through</tt>.
+ # * <tt>:readonly</tt> - If true, all the associated objects are readonly through the association.
#
# Option examples:
# has_many :comments, :order => "posted_on"
@@ -712,14 +712,14 @@ module ActiveRecord
# Adds the following methods for retrieval and query of a single associated object:
# +association+ is replaced with the symbol passed as the first argument, so
# <tt>has_one :manager</tt> would add among others <tt>manager.nil?</tt>.
- # * <tt>association(force_reload = false)</tt> - returns the associated object. +nil+ is returned if none is found.
- # * <tt>association=(associate)</tt> - assigns the associate object, extracts the primary key, sets it as the foreign key,
+ # * <tt>association(force_reload = false)</tt> - Returns the associated object. +nil+ is returned if none is found.
+ # * <tt>association=(associate)</tt> - Assigns the associate object, extracts the primary key, sets it as the foreign key,
# and saves the associate object.
- # * <tt>association.nil?</tt> - returns +true+ if there is no associated object.
- # * <tt>build_association(attributes = {})</tt> - returns a new object of the associated type that has been instantiated
+ # * <tt>association.nil?</tt> - Returns +true+ if there is no associated object.
+ # * <tt>build_association(attributes = {})</tt> - Returns a new object of the associated type that has been instantiated
# with +attributes+ and linked to this object through a foreign key, but has not yet been saved. Note: This ONLY works if
# an association already exists. It will NOT work if the association is +nil+.
- # * <tt>create_association(attributes = {})</tt> - returns a new object of the associated type that has been instantiated
+ # * <tt>create_association(attributes = {})</tt> - Returns a new object of the associated type that has been instantiated
# with +attributes+, linked to this object through a foreign key, and that has already been saved (if it passed the validation).
#
# Example: An Account class declares <tt>has_one :beneficiary</tt>, which will add:
@@ -732,28 +732,28 @@ module ActiveRecord
# The declaration can also include an options hash to specialize the behavior of the association.
#
# Options are:
- # * <tt>:class_name</tt> - specify the class name of the association. Use it only if that name can't be inferred
- # from the association name. So <tt>has_one :manager</tt> will by default be linked to the +Manager+ class, but
- # if the real class name is +Person+, you'll have to specify it with this option.
- # * <tt>:conditions</tt> - specify the conditions that the associated object must meet in order to be included as a +WHERE+
+ # * <tt>:class_name</tt> - Specify the class name of the association. Use it only if that name can't be inferred
+ # from the association name. So <tt>has_one :manager</tt> will by default be linked to the Manager class, but
+ # if the real class name is Person, you'll have to specify it with this option.
+ # * <tt>:conditions</tt> - Specify the conditions that the associated object must meet in order to be included as a +WHERE+
# SQL fragment, such as <tt>rank = 5</tt>.
- # * <tt>:order</tt> - specify the order in which the associated objects are returned as an <tt>ORDER BY</tt> SQL fragment,
- # such as <tt>last_name, first_name DESC</tt>
- # * <tt>:dependent</tt> - if set to <tt>:destroy</tt>, the associated object is destroyed when this object is. If set to
+ # * <tt>:order</tt> - Specify the order in which the associated objects are returned as an <tt>ORDER BY</tt> SQL fragment,
+ # such as <tt>last_name, first_name DESC</tt>.
+ # * <tt>:dependent</tt> - If set to <tt>:destroy</tt>, the associated object is destroyed when this object is. If set to
# <tt>:delete</tt>, the associated object is deleted *without* calling its destroy method. If set to <tt>:nullify</tt>, the associated
# object's foreign key is set to +NULL+. Also, association is assigned.
- # * <tt>:foreign_key</tt> - specify the foreign key used for the association. By default this is guessed to be the name
- # of this class in lower-case and +_id+ suffixed. So a +Person+ class that makes a +has_one+ association will use +person_id+
- # as the default +foreign_key+.
- # * <tt>:include</tt> - specify second-order associations that should be eager loaded when this object is loaded.
- # * <tt>:as</tt>: Specifies a polymorphic interface (See <tt>#belongs_to</tt>).
+ # * <tt>:foreign_key</tt> - Specify the foreign key used for the association. By default this is guessed to be the name
+ # of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_one+ association will use "person_id"
+ # as the default <tt>:foreign_key</tt>.
+ # * <tt>:include</tt> - Specify second-order associations that should be eager loaded when this object is loaded.
+ # * <tt>:as</tt> - Specifies a polymorphic interface (See <tt>belongs_to</tt>).
# * <tt>:through</tt>: Specifies a Join Model through which to perform the query. Options for <tt>:class_name</tt> and <tt>:foreign_key</tt>
# are ignored, as the association uses the source reflection. You can only use a <tt>:through</tt> query through a
# <tt>has_one</tt> or <tt>belongs_to</tt> association on the join model.
- # * <tt>:source</tt>: Specifies the source association name used by <tt>has_one :through</tt> queries. Only use it if the name cannot be
+ # * <tt>:source</tt> - Specifies the source association name used by <tt>has_one :through</tt> queries. Only use it if the name cannot be
# inferred from the association. <tt>has_one :favorite, :through => :favorites</tt> will look for a
- # <tt>:favorite</tt> on +Favorite+, unless a <tt>:source</tt> is given.
- # * <tt>:readonly</tt> - if set to +true+, the associated object is readonly through the association.
+ # <tt>:favorite</tt> on Favorite, unless a <tt>:source</tt> is given.
+ # * <tt>:readonly</tt> - If true, the associated object is readonly through the association.
#
# Option examples:
# has_one :credit_card, :dependent => :destroy # destroys the associated credit card
@@ -796,12 +796,12 @@ module ActiveRecord
# Adds the following methods for retrieval and query for a single associated object for which this object holds an id:
# +association+ is replaced with the symbol passed as the first argument, so
# <tt>belongs_to :author</tt> would add among others <tt>author.nil?</tt>.
- # * <tt>association(force_reload = false)</tt> - returns the associated object. +nil+ is returned if none is found.
- # * <tt>association=(associate)</tt> - assigns the associate object, extracts the primary key, and sets it as the foreign key.
- # * <tt>association.nil?</tt> - returns +true+ if there is no associated object.
- # * <tt>build_association(attributes = {})</tt> - returns a new object of the associated type that has been instantiated
+ # * <tt>association(force_reload = false)</tt> - Returns the associated object. +nil+ is returned if none is found.
+ # * <tt>association=(associate)</tt> - Assigns the associate object, extracts the primary key, and sets it as the foreign key.
+ # * <tt>association.nil?</tt> - Returns +true+ if there is no associated object.
+ # * <tt>build_association(attributes = {})</tt> - Returns a new object of the associated type that has been instantiated
# with +attributes+ and linked to this object through a foreign key, but has not yet been saved.
- # * <tt>create_association(attributes = {})</tt> - returns a new object of the associated type that has been instantiated
+ # * <tt>create_association(attributes = {})</tt> - Returns a new object of the associated type that has been instantiated
# with +attributes+, linked to this object through a foreign key, and that has already been saved (if it passed the validation).
#
# Example: A Post class declares <tt>belongs_to :author</tt>, which will add:
@@ -814,34 +814,35 @@ module ActiveRecord
# The declaration can also include an options hash to specialize the behavior of the association.
#
# Options are:
- # * <tt>:class_name</tt> - specify the class name of the association. Use it only if that name can't be inferred
- # from the association name. So <tt>has_one :author</tt> will by default be linked to the +Author+ class, but
- # if the real class name is +Person+, you'll have to specify it with this option.
- # * <tt>:conditions</tt> - specify the conditions that the associated object must meet in order to be included as a +WHERE+
+ # * <tt>:class_name</tt> - Specify the class name of the association. Use it only if that name can't be inferred
+ # from the association name. So <tt>has_one :author</tt> will by default be linked to the Author class, but
+ # if the real class name is Person, you'll have to specify it with this option.
+ # * <tt>:conditions</tt> - Specify the conditions that the associated object must meet in order to be included as a +WHERE+
# SQL fragment, such as <tt>authorized = 1</tt>.
- # * <tt>:order</tt> - specify the order in which the associated objects are returned as an <tt>ORDER BY</tt> SQL fragment,
- # such as <tt>last_name, first_name DESC</tt>
- # * <tt>:foreign_key</tt> - specify the foreign key used for the association. By default this is guessed to be the name
- # of the association with an +_id+ suffix. So a class that defines a +belongs_to :person+ association will use +person_id+ as the default +foreign_key+.
- # Similarly, +belongs_to :favorite_person, :class_name => "Person"+ will use a foreign key of +favorite_person_id+.
- # * <tt>:dependent</tt> - if set to <tt>:destroy</tt>, the associated object is destroyed when this object is. If set to
+ # * <tt>:order</tt> - Specify the order in which the associated objects are returned as an <tt>ORDER BY</tt> SQL fragment,
+ # such as <tt>last_name, first_name DESC</tt>.
+ # * <tt>:foreign_key</tt> - Specify the foreign key used for the association. By default this is guessed to be the name
+ # of the association with an "_id" suffix. So a class that defines a <tt>belongs_to :person</tt> association will use
+ # "person_id" as the default <tt>:foreign_key</tt>. Similarly, <tt>belongs_to :favorite_person, :class_name => "Person"</tt>
+ # will use a foreign key of "favorite_person_id".
+ # * <tt>:dependent</tt> - If set to <tt>:destroy</tt>, the associated object is destroyed when this object is. If set to
# <tt>:delete</tt>, the associated object is deleted *without* calling its destroy method. This option should not be specified when
# <tt>belongs_to</tt> is used in conjunction with a <tt>has_many</tt> relationship on another class because of the potential to leave
# orphaned records behind.
- # * <tt>:counter_cache</tt> - caches the number of belonging objects on the associate class through the use of +increment_counter+
+ # * <tt>:counter_cache</tt> - Caches the number of belonging objects on the associate class through the use of +increment_counter+
# and +decrement_counter+. The counter cache is incremented when an object of this class is created and decremented when it's
- # destroyed. This requires that a column named <tt>#{table_name}_count</tt> (such as +comments_count+ for a belonging +Comment+ class)
- # is used on the associate class (such as a +Post+ class). You can also specify a custom counter cache column by providing
+ # destroyed. This requires that a column named <tt>#{table_name}_count</tt> (such as +comments_count+ for a belonging Comment class)
+ # is used on the associate class (such as a Post class). You can also specify a custom counter cache column by providing
# a column name instead of a +true+/+false+ value to this option (e.g., <tt>:counter_cache => :my_custom_counter</tt>.)
# When creating a counter cache column, the database statement or migration must specify a default value of <tt>0</tt>, failing to do
- # this results in a counter with NULL value, which will never increment.
- # Note: Specifying a counter_cache will add it to that model's list of readonly attributes using #attr_readonly.
- # * <tt>:include</tt> - specify second-order associations that should be eager loaded when this object is loaded.
+ # this results in a counter with +NULL+ value, which will never increment.
+ # Note: Specifying a counter cache will add it to that model's list of readonly attributes using +attr_readonly+.
+ # * <tt>:include</tt> - Specify second-order associations that should be eager loaded when this object is loaded.
# Not allowed if the association is polymorphic.
- # * <tt>:polymorphic</tt> - specify this association is a polymorphic association by passing +true+.
+ # * <tt>:polymorphic</tt> - Specify this association is a polymorphic association by passing +true+.
# Note: If you've enabled the counter cache, then you may want to add the counter cache attribute
- # to the attr_readonly list in the associated classes (e.g. class Post; attr_readonly :comments_count; end).
- # * <tt>:readonly</tt> - if set to +true+, the associated object is readonly through the association.
+ # to the +attr_readonly+ list in the associated classes (e.g. <tt>class Post; attr_readonly :comments_count; end</tt>).
+ # * <tt>:readonly</tt> - If true, the associated object is readonly through the association.
#
# Option examples:
# belongs_to :firm, :foreign_key => "client_of"
@@ -926,14 +927,14 @@ module ActiveRecord
end
# Associates two classes via an intermediate join table. Unless the join table is explicitly specified as
- # an option, it is guessed using the lexical order of the class names. So a join between +Developer+ and +Project+
- # will give the default join table name of +developers_projects+ because "D" outranks "P". Note that this precedence
- # is calculated using the <tt><</tt> operator for <tt>String</tt>. This means that if the strings are of different lengths,
+ # an option, it is guessed using the lexical order of the class names. So a join between Developer and Project
+ # will give the default join table name of "developers_projects" because "D" outranks "P". Note that this precedence
+ # is calculated using the <tt><</tt> operator for String. This means that if the strings are of different lengths,
# and the strings are equal when compared up to the shortest length, then the longer string is considered of higher
- # lexical precedence than the shorter one. For example, one would expect the tables <tt>paper_boxes</tt> and <tt>papers</tt>
- # to generate a join table name of <tt>papers_paper_boxes</tt> because of the length of the name <tt>paper_boxes</tt>,
- # but it in fact generates a join table name of <tt>paper_boxes_papers</tt>. Be aware of this caveat, and use the
- # custom <tt>join_table</tt> option if you need to.
+ # lexical precedence than the shorter one. For example, one would expect the tables "paper_boxes" and "papers"
+ # to generate a join table name of "papers_paper_boxes" because of the length of the name "paper_boxes",
+ # but it in fact generates a join table name of "paper_boxes_papers". Be aware of this caveat, and use the
+ # custom <tt>:join_table</tt> option if you need to.
#
# Deprecated: Any additional fields added to the join table will be placed as attributes when pulling records out through
# +has_and_belongs_to_many+ associations. Records returned from join tables with additional attributes will be marked as
@@ -943,23 +944,23 @@ module ActiveRecord
# Adds the following methods for retrieval and query:
# +collection+ is replaced with the symbol passed as the first argument, so
# <tt>has_and_belongs_to_many :categories</tt> would add among others <tt>categories.empty?</tt>.
- # * <tt>collection(force_reload = false)</tt> - returns an array of all the associated objects.
+ # * <tt>collection(force_reload = false)</tt> - Returns an array of all the associated objects.
# An empty array is returned if none are found.
- # * <tt>collection<<(object, ...)</tt> - adds one or more objects to the collection by creating associations in the join table
+ # * <tt>collection<<(object, ...)</tt> - Adds one or more objects to the collection by creating associations in the join table
# (<tt>collection.push</tt> and <tt>collection.concat</tt> are aliases to this method).
- # * <tt>collection.delete(object, ...)</tt> - removes one or more objects from the collection by removing their associations from the join table.
+ # * <tt>collection.delete(object, ...)</tt> - Removes one or more objects from the collection by removing their associations from the join table.
# This does not destroy the objects.
- # * <tt>collection=objects</tt> - replaces the collection's content by deleting and adding objects as appropriate.
- # * <tt>collection_singular_ids</tt> - returns an array of the associated objects' ids
- # * <tt>collection_singular_ids=ids</tt> - replace the collection by the objects identified by the primary keys in +ids+
- # * <tt>collection.clear</tt> - removes every object from the collection. This does not destroy the objects.
- # * <tt>collection.empty?</tt> - returns +true+ if there are no associated objects.
- # * <tt>collection.size</tt> - returns the number of associated objects.
- # * <tt>collection.find(id)</tt> - finds an associated object responding to the +id+ and that
+ # * <tt>collection=objects</tt> - Replaces the collection's content by deleting and adding objects as appropriate.
+ # * <tt>collection_singular_ids</tt> - Returns an array of the associated objects' ids.
+ # * <tt>collection_singular_ids=ids</tt> - Replace the collection by the objects identified by the primary keys in +ids+.
+ # * <tt>collection.clear</tt> - Removes every object from the collection. This does not destroy the objects.
+ # * <tt>collection.empty?</tt> - Returns +true+ if there are no associated objects.
+ # * <tt>collection.size</tt> - Returns the number of associated objects.
+ # * <tt>collection.find(id)</tt> - Finds an associated object responding to the +id+ and that
# meets the condition that it has to be associated with this object.
- # * <tt>collection.build(attributes = {})</tt> - returns a new object of the collection type that has been instantiated
+ # * <tt>collection.build(attributes = {})</tt> - Returns a new object of the collection type that has been instantiated
# with +attributes+ and linked to this object through the join table, but has not yet been saved.
- # * <tt>collection.create(attributes = {})</tt> - returns a new object of the collection type that has been instantiated
+ # * <tt>collection.create(attributes = {})</tt> - Returns a new object of the collection type that has been instantiated
# with +attributes+, linked to this object through the join table, and that has already been saved (if it passed the validation).
#
# Example: A Developer class declares <tt>has_and_belongs_to_many :projects</tt>, which will add:
@@ -978,38 +979,38 @@ module ActiveRecord
# The declaration may include an options hash to specialize the behavior of the association.
#
# Options are:
- # * <tt>:class_name</tt> - specify the class name of the association. Use it only if that name can't be inferred
+ # * <tt>:class_name</tt> - Specify the class name of the association. Use it only if that name can't be inferred
# from the association name. So <tt>has_and_belongs_to_many :projects</tt> will by default be linked to the
- # +Project+ class, but if the real class name is +SuperProject+, you'll have to specify it with this option.
- # * <tt>:join_table</tt> - specify the name of the join table if the default based on lexical order isn't what you want.
+ # Project class, but if the real class name is SuperProject, you'll have to specify it with this option.
+ # * <tt>:join_table</tt> - Specify the name of the join table if the default based on lexical order isn't what you want.
# WARNING: If you're overwriting the table name of either class, the +table_name+ method MUST be declared underneath any
# +has_and_belongs_to_many+ declaration in order to work.
- # * <tt>:foreign_key</tt> - specify the foreign key used for the association. By default this is guessed to be the name
- # of this class in lower-case and +_id+ suffixed. So a +Person+ class that makes a +has_and_belongs_to_many+ association
- # will use +person_id+ as the default +foreign_key+.
- # * <tt>:association_foreign_key</tt> - specify the association foreign key used for the association. By default this is
- # guessed to be the name of the associated class in lower-case and +_id+ suffixed. So if the associated class is +Project+,
- # the +has_and_belongs_to_many+ association will use +project_id+ as the default association +foreign_key+.
- # * <tt>:conditions</tt> - specify the conditions that the associated object must meet in order to be included as a +WHERE+
+ # * <tt>:foreign_key</tt> - Specify the foreign key used for the association. By default this is guessed to be the name
+ # of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_and_belongs_to_many+ association
+ # will use "person_id" as the default <tt>:foreign_key</tt>.
+ # * <tt>:association_foreign_key</tt> - Specify the association foreign key used for the association. By default this is
+ # guessed to be the name of the associated class in lower-case and "_id" suffixed. So if the associated class is Project,
+ # the +has_and_belongs_to_many+ association will use "project_id" as the default <tt>:association_foreign_key</tt>.
+ # * <tt>:conditions</tt> - Specify the conditions that the associated object must meet in order to be included as a +WHERE+
# SQL fragment, such as <tt>authorized = 1</tt>. Record creations from the association are scoped if a hash is used.
# <tt>has_many :posts, :conditions => {:published => true}</tt> will create published posts with <tt>@blog.posts.create</tt>
# or <tt>@blog.posts.build</tt>.
- # * <tt>:order</tt> - specify the order in which the associated objects are returned as an <tt>ORDER BY</tt> SQL fragment,
+ # * <tt>:order</tt> - Specify the order in which the associated objects are returned as an <tt>ORDER BY</tt> SQL fragment,
# such as <tt>last_name, first_name DESC</tt>
- # * <tt>:uniq</tt> - if set to +true+, duplicate associated objects will be ignored by accessors and query methods
- # * <tt>:finder_sql</tt> - overwrite the default generated SQL statement used to fetch the association with a manual statement
- # * <tt>:delete_sql</tt> - overwrite the default generated SQL statement used to remove links between the associated
- # classes with a manual statement
- # * <tt>:insert_sql</tt> - overwrite the default generated SQL statement used to add links between the associated classes
- # with a manual statement
- # * <tt>:extend</tt> - anonymous module for extending the proxy, see "Association extensions".
- # * <tt>:include</tt> - specify second-order associations that should be eager loaded when the collection is loaded.
- # * <tt>:group</tt>: An attribute name by which the result should be grouped. Uses the <tt>GROUP BY</tt> SQL-clause.
- # * <tt>:limit</tt>: An integer determining the limit on the number of rows that should be returned.
- # * <tt>:offset</tt>: An integer determining the offset from where the rows should be fetched. So at 5, it would skip the first 4 rows.
- # * <tt>:select</tt>: By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed if, for example, you want to do a join
+ # * <tt>:uniq</tt> - If true, duplicate associated objects will be ignored by accessors and query methods.
+ # * <tt>:finder_sql</tt> - Overwrite the default generated SQL statement used to fetch the association with a manual statement
+ # * <tt>:delete_sql</tt> - Overwrite the default generated SQL statement used to remove links between the associated
+ # classes with a manual statement.
+ # * <tt>:insert_sql</tt> - Overwrite the default generated SQL statement used to add links between the associated classes
+ # with a manual statement.
+ # * <tt>:extend</tt> - Anonymous module for extending the proxy, see "Association extensions".
+ # * <tt>:include</tt> - Specify second-order associations that should be eager loaded when the collection is loaded.
+ # * <tt>:group</tt> - An attribute name by which the result should be grouped. Uses the <tt>GROUP BY</tt> SQL-clause.
+ # * <tt>:limit</tt> - An integer determining the limit on the number of rows that should be returned.
+ # * <tt>:offset</tt> - An integer determining the offset from where the rows should be fetched. So at 5, it would skip the first 4 rows.
+ # * <tt>:select</tt> - By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed if, for example, you want to do a join
# but not include the joined columns.
- # * <tt>:readonly</tt> - if set to +true+, all the associated objects are readonly through the association.
+ # * <tt>:readonly</tt> - If true, all the associated objects are readonly through the association.
#
# Option examples:
# has_and_belongs_to_many :projects
@@ -1446,6 +1447,12 @@ module ActiveRecord
tables_from_conditions = conditions_tables(options)
tables_from_order = order_tables(options)
all_tables = tables_from_conditions + tables_from_order
+ distinct_join_associations = all_tables.uniq.map{|table|
+ join_dependency.joins_for_table_name(table)
+ }.flatten.compact.uniq
+
+
+
is_distinct = !options[:joins].blank? || include_eager_conditions?(options, tables_from_conditions) || include_eager_order?(options, tables_from_order)
sql = "SELECT "
@@ -1457,7 +1464,7 @@ module ActiveRecord
sql << " FROM #{connection.quote_table_name table_name} "
if is_distinct
- sql << join_dependency.join_associations.reject{ |ja| !all_tables.include?(ja.table_name) }.collect(&:association_join).join
+ sql << distinct_join_associations.collect(&:association_join).join
add_joins!(sql, options, scope)
end
@@ -1617,6 +1624,23 @@ module ActiveRecord
end
end
+ def join_for_table_name(table_name)
+ @joins.select{|j|j.aliased_table_name == table_name.gsub(/^\"(.*)\"$/){$1} }.first rescue nil
+ end
+
+ def joins_for_table_name(table_name)
+ join = join_for_table_name(table_name)
+ result = nil
+ if join && join.is_a?(JoinAssociation)
+ result = [join]
+ if join.parent && join.parent.is_a?(JoinAssociation)
+ result = joins_for_table_name(join.parent.aliased_table_name) +
+ result
+ end
+ end
+ result
+ end
+
protected
def build(associations, parent = nil)
parent ||= @joins.last
diff --git a/activerecord/lib/active_record/associations/association_proxy.rb b/activerecord/lib/active_record/associations/association_proxy.rb
index c415ad2df3..68503a3c40 100644
--- a/activerecord/lib/active_record/associations/association_proxy.rb
+++ b/activerecord/lib/active_record/associations/association_proxy.rb
@@ -118,7 +118,7 @@ module ActiveRecord
end
def inspect
- reload unless loaded?
+ load_target
@target.inspect
end
@@ -167,7 +167,7 @@ module ActiveRecord
def with_scope(*args, &block)
@reflection.klass.send :with_scope, *args, &block
end
-
+
private
def method_missing(method, *args)
if load_target
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index 46ecfc1969..2db27226f2 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -179,10 +179,10 @@ module ActiveRecord
def define_write_method_for_time_zone_conversion(attr_name)
method_body = <<-EOV
def #{attr_name}=(time)
- if time
- time = time.to_time rescue time unless time.acts_like?(:time)
- time = time.in_time_zone if time.acts_like?(:time)
+ unless time.acts_like?(:time)
+ time = time.is_a?(String) ? Time.zone.parse(time) : time.to_time rescue time
end
+ time = time.in_time_zone rescue nil if time
write_attribute(:#{attr_name}, time)
end
EOV
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index ffefc3cef3..74299bd572 100755
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -92,13 +92,15 @@ module ActiveRecord #:nodoc:
class DangerousAttributeError < ActiveRecordError
end
- # Raised when you've tried to access a column which wasn't
- # loaded by your finder. Typically this is because <tt>:select</tt>
- # has been specified.
+ # Raised when you've tried to access a column which wasn't loaded by your finder.
+ # Typically this is because <tt>:select</tt> has been specified.
class MissingAttributeError < NoMethodError
end
- class AttributeAssignmentError < ActiveRecordError #:nodoc:
+ # Raised when an error occured 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
attr_reader :exception, :attribute
def initialize(message, exception, attribute)
@exception = exception
@@ -107,7 +109,10 @@ module ActiveRecord #:nodoc:
end
end
- class MultiparameterAssignmentErrors < ActiveRecordError #:nodoc:
+ # Raised when there are multiple errors while doing a mass assignment through the +attributes+
+ # method. The exception has an +errors+ property that contains an array of AttributeAssignmentError
+ # objects, each corresponding to the error while assigning to an attribute.
+ class MultiparameterAssignmentErrors < ActiveRecordError
attr_reader :errors
def initialize(errors)
@errors = errors
@@ -230,8 +235,8 @@ module ActiveRecord #:nodoc:
# == Accessing attributes before they have been typecasted
#
# Sometimes you want to be able to read the raw attribute data without having the column-determined typecast run its course first.
- # That can be done by using the <attribute>_before_type_cast accessors that all attributes have. For example, if your Account model
- # has a balance attribute, you can call account.balance_before_type_cast or account.id_before_type_cast.
+ # That can be done by using the <tt><attribute>_before_type_cast</tt> accessors that all attributes have. For example, if your Account model
+ # has a balance attribute, you can call <tt>account.balance_before_type_cast</tt> or <tt>account.id_before_type_cast</tt>.
#
# This is especially useful in validation situations where the user might supply a string for an integer field and you want to display
# the original string back in an error message. Accessing the attribute normally would typecast the string to 0, which isn't what you
@@ -332,26 +337,26 @@ module ActiveRecord #:nodoc:
#
# == Exceptions
#
- # * +ActiveRecordError+ -- generic error class and superclass of all other errors raised by Active Record
- # * +AdapterNotSpecified+ -- the configuration hash used in <tt>establish_connection</tt> didn't include an
+ # * ActiveRecordError - Generic error class and superclass of all other errors raised by Active Record.
+ # * AdapterNotSpecified - The configuration hash used in <tt>establish_connection</tt> didn't include an
# <tt>:adapter</tt> key.
- # * +AdapterNotFound+ -- the <tt>:adapter</tt> key used in <tt>establish_connection</tt> specified a non-existent adapter
+ # * AdapterNotFound - The <tt>:adapter</tt> key used in <tt>establish_connection</tt> specified a non-existent adapter
# (or a bad spelling of an existing one).
- # * +AssociationTypeMismatch+ -- the object assigned to the association wasn't of the type specified in the association definition.
- # * +SerializationTypeMismatch+ -- the serialized object wasn't of the class specified as the second parameter.
- # * +ConnectionNotEstablished+ -- no connection has been established. Use <tt>establish_connection</tt> before querying.
- # * +RecordNotFound+ -- no record responded to the find* method.
- # Either the row with the given ID doesn't exist or the row didn't meet the additional restrictions.
- # * +StatementInvalid+ -- the database server rejected the SQL statement. The precise error is added in the message.
- # Either the record with the given ID doesn't exist or the record didn't meet the additional restrictions.
- # * +MultiparameterAssignmentErrors+ -- collection of errors that occurred during a mass assignment using the
- # +attributes=+ method. The +errors+ property of this exception contains an array of +AttributeAssignmentError+
+ # * AssociationTypeMismatch - The object assigned to the association wasn't of the type specified in the association definition.
+ # * SerializationTypeMismatch - The serialized object wasn't of the class specified as the second parameter.
+ # * ConnectionNotEstablished+ - No connection has been established. Use <tt>establish_connection</tt> before querying.
+ # * RecordNotFound - No record responded to the +find+ method. Either the row with the given ID doesn't exist
+ # or the row didn't meet the additional restrictions. Some +find+ calls do not raise this exception to signal
+ # nothing was found, please check its documentation for further details.
+ # * StatementInvalid - The database server rejected the SQL statement. The precise error is added in the message.
+ # * MultiparameterAssignmentErrors - Collection of errors that occurred during a mass assignment using the
+ # <tt>attributes=</tt> method. The +errors+ property of this exception contains an array of AttributeAssignmentError
# objects that should be inspected to determine which attributes triggered the errors.
- # * +AttributeAssignmentError+ -- an error occurred while doing a mass assignment through the +attributes=+ method.
+ # * AttributeAssignmentError - An error occurred while doing a mass assignment through the <tt>attributes=</tt> method.
# You can inspect the +attribute+ property of the exception object to determine which attribute triggered the error.
#
# *Note*: The attributes listed are class-level attributes (accessible from both the class and instance level).
- # So it's possible to assign a logger to the class through Base.logger= which will then be used by all
+ # So it's possible to assign a logger to the class through <tt>Base.logger=</tt> which will then be used by all
# instances in the current object space.
class Base
# Accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class, which is then passed
@@ -601,7 +606,7 @@ module ActiveRecord #:nodoc:
# User.create(:first_name => 'Jamie')
#
# # Create an Array of new objects
- # User.create([{:first_name => 'Jamie'}, {:first_name => 'Jeremy'}])
+ # User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }])
#
# # Create a single object and pass it into a block to set other attributes.
# User.create(:first_name => 'Jamie') do |u|
@@ -609,7 +614,7 @@ module ActiveRecord #:nodoc:
# end
#
# # Creating an Array of new objects using a block, where the block is executed for each object:
- # User.create([{:first_name => 'Jamie'}, {:first_name => 'Jeremy'}]) do |u|
+ # User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }]) do |u|
# u.is_admin = false
# end
def create(attributes = nil, &block)
@@ -626,18 +631,18 @@ module ActiveRecord #:nodoc:
# Updates an object (or multiple objects) and saves it to the database, if validations pass.
# The resulting object is returned whether the object was saved successfully to the database or not.
#
- # ==== Options
+ # ==== Attributes
#
- # +id+ This should be the id or an array of ids to be updated
- # +attributes+ This should be a Hash of attributes to be set on the object, or an array of Hashes.
+ # * +id+ - This should be the id or an array of ids to be updated.
+ # * +attributes+ - This should be a Hash of attributes to be set on the object, or an array of Hashes.
#
# ==== Examples
#
# # Updating one record:
- # Person.update(15, {:user_name => 'Samuel', :group => 'expert'})
+ # Person.update(15, { :user_name => 'Samuel', :group => 'expert' })
#
# # Updating multiple records:
- # people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy"} }
+ # people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy" } }
# Person.update(people.keys, people.values)
def update(id, attributes)
if id.is_a?(Array)
@@ -656,9 +661,9 @@ module ActiveRecord #:nodoc:
#
# Objects are _not_ instantiated with this method.
#
- # ==== Options
+ # ==== Attributes
#
- # +id+ Can be either an Integer or an Array of Integers
+ # * +id+ - Can be either an Integer or an Array of Integers.
#
# ==== Examples
#
@@ -679,9 +684,9 @@ module ActiveRecord #:nodoc:
# This essentially finds the object (or multiple objects) with the given id, creates a new object
# from the attributes, and then calls destroy on it.
#
- # ==== Options
+ # ==== Attributes
#
- # +id+ Can be either an Integer or an Array of Integers
+ # * +id+ - Can be either an Integer or an Array of Integers.
#
# ==== Examples
#
@@ -702,12 +707,12 @@ module ActiveRecord #:nodoc:
# Updates all records with details given if they match a set of conditions supplied, limits and order can
# also be supplied.
#
- # ==== Options
+ # ==== 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.
- # +options+ Additional options are <tt>:limit</tt> and/or <tt>:order</tt>, see the examples for usage.
+ # * +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.
+ # * +options+ - Additional options are <tt>:limit</tt> and/or <tt>:order</tt>, see the examples for usage.
#
# ==== Examples
#
@@ -734,9 +739,9 @@ module ActiveRecord #:nodoc:
# many records. If you want to simply delete records without worrying about dependent associations or
# callbacks, use the much faster +delete_all+ method instead.
#
- # ==== Options
+ # ==== Attributes
#
- # +conditions+ Conditions are specified the same way as with +find+ method.
+ # * +conditions+ - Conditions are specified the same way as with +find+ method.
#
# ==== Example
#
@@ -752,9 +757,9 @@ module ActiveRecord #:nodoc:
# calling the destroy method and invoking callbacks. This is a single SQL query, much more efficient
# than destroy_all.
#
- # ==== Options
+ # ==== Attributes
#
- # +conditions+ Conditions are specified the same way as with +find+ method.
+ # * +conditions+ - Conditions are specified the same way as with +find+ method.
#
# ==== Example
#
@@ -772,9 +777,9 @@ module ActiveRecord #:nodoc:
# The use of this method should be restricted to complicated SQL queries that can't be executed
# using the ActiveRecord::Calculations class methods. Look into those before using this.
#
- # ==== Options
+ # ==== Attributes
#
- # +sql+: An SQL statement which should return a count query from the database, see the example below
+ # * +sql+ - An SQL statement which should return a count query from the database, see the example below.
#
# ==== Examples
#
@@ -790,12 +795,11 @@ module ActiveRecord #:nodoc:
# with the given ID, altering the given hash of counters by the amount
# given by the corresponding value:
#
- # ==== Options
+ # ==== Attributes
#
- # +id+ The id of the object you wish to update a counter on
- # +counters+ An Array of Hashes containing the names of the fields
- # to update as keys and the amount to update the field by as
- # values
+ # * +id+ - The id of the object you wish to update a counter on.
+ # * +counters+ - An Array of Hashes containing the names of the fields
+ # to update as keys and the amount to update the field by as values.
#
# ==== Examples
#
@@ -821,10 +825,10 @@ module ActiveRecord #:nodoc:
# For example, a DiscussionBoard may cache post_count and comment_count otherwise every time the board is
# shown it would have to run an SQL query to find how many posts and comments there are.
#
- # ==== Options
+ # ==== Attributes
#
- # +counter_name+ The name of the field that should be incremented
- # +id+ The id of the object that should be incremented
+ # * +counter_name+ - The name of the field that should be incremented.
+ # * +id+ - The id of the object that should be incremented.
#
# ==== Examples
#
@@ -838,10 +842,10 @@ module ActiveRecord #:nodoc:
#
# This works the same as increment_counter but reduces the column value by 1 instead of increasing it.
#
- # ==== Options
+ # ==== Attributes
#
- # +counter_name+ The name of the field that should be decremented
- # +id+ The id of the object that should be decremented
+ # * +counter_name+ - The name of the field that should be decremented.
+ # * +id+ - The id of the object that should be decremented.
#
# ==== Examples
#
@@ -886,9 +890,9 @@ module ActiveRecord #:nodoc:
# overwritten by URL/form hackers. If you'd rather start from an all-open default and restrict
# attributes as needed, have a look at attr_protected.
#
- # ==== Options
+ # ==== Attributes
#
- # <tt>*attributes</tt> A comma separated list of symbols that represent columns _not_ to be protected
+ # * <tt>*attributes</tt> A comma separated list of symbols that represent columns _not_ to be protected
#
# ==== Examples
#
@@ -927,10 +931,10 @@ module ActiveRecord #:nodoc:
# The serialization is done through YAML. If +class_name+ is specified, the serialized object must be of that
# class on retrieval or +SerializationTypeMismatch+ will be raised.
#
- # ==== Options
+ # ==== Attributes
#
- # +attr_name+ The field name that should be serialized
- # +class_name+ Optional, class name that the object type should be equal to
+ # * +attr_name+ - The field name that should be serialized.
+ # * +class_name+ - Optional, class name that the object type should be equal to.
#
# ==== Example
# # Serialize a preferences attribute
@@ -1118,18 +1122,7 @@ module ActiveRecord #:nodoc:
# Indicates whether the table associated with this class exists
def table_exists?
- if connection.respond_to?(:tables)
- connection.tables.include? table_name
- else
- # if the connection adapter hasn't implemented tables, there are two crude tests that can be
- # used - see if getting column info raises an error, or if the number of columns returned is zero
- begin
- reset_column_information
- columns.size > 0
- rescue ActiveRecord::StatementInvalid
- false
- end
- end
+ connection.table_exists?(table_name)
end
# Returns an array of column objects for the table associated with this class.
@@ -1768,7 +1761,7 @@ module ActiveRecord #:nodoc:
# class Article < ActiveRecord::Base
# def self.find_with_scope
# with_scope(:find => { :conditions => "blog_id = 1", :limit => 1 }, :create => { :blog_id => 1 }) do
- # with_scope(:find => { :limit => 10})
+ # with_scope(:find => { :limit => 10 })
# find(:all) # => SELECT * from articles WHERE blog_id = 1 LIMIT 10
# end
# with_scope(:find => { :conditions => "author_id = 3" })
@@ -2249,40 +2242,53 @@ module ActiveRecord #:nodoc:
save!
end
- # Initializes the +attribute+ to zero if nil and adds the value passed as +by+ (default is one). Only makes sense for number-based attributes. Returns self.
+ # Initializes +attribute+ to zero if +nil+ and adds the value passed as +by+ (default is 1).
+ # The increment is performed directly on the underlying attribute, no setter is invoked.
+ # Only makes sense for number-based attributes. Returns +self+.
def increment(attribute, by = 1)
self[attribute] ||= 0
self[attribute] += by
self
end
- # Increments the +attribute+ and saves the record.
- # Note: Updates made with this method aren't subjected to validation checks
+ # Wrapper around +increment+ that saves the record. This method differs from
+ # its non-bang version in that it passes through the attribute setter.
+ # Saving is not subjected to validation checks. Returns +true+ if the
+ # record could be saved.
def increment!(attribute, by = 1)
increment(attribute, by).update_attribute(attribute, self[attribute])
end
- # Initializes the +attribute+ to zero if nil and subtracts the value passed as +by+ (default is one). Only makes sense for number-based attributes. Returns self.
+ # Initializes +attribute+ to zero if +nil+ and subtracts the value passed as +by+ (default is 1).
+ # The decrement is performed directly on the underlying attribute, no setter is invoked.
+ # Only makes sense for number-based attributes. Returns +self+.
def decrement(attribute, by = 1)
self[attribute] ||= 0
self[attribute] -= by
self
end
- # Decrements the +attribute+ and saves the record.
- # Note: Updates made with this method aren't subjected to validation checks
+ # Wrapper around +decrement+ that saves the record. This method differs from
+ # its non-bang version in that it passes through the attribute setter.
+ # Saving is not subjected to validation checks. Returns +true+ if the
+ # record could be saved.
def decrement!(attribute, by = 1)
decrement(attribute, by).update_attribute(attribute, self[attribute])
end
- # Turns an +attribute+ that's currently true into false and vice versa. Returns self.
+ # Assigns to +attribute+ the boolean opposite of <tt>attribute?</tt>. So
+ # if the predicate returns +true+ the attribute will become +false+. This
+ # method toggles directly the underlying value without calling any setter.
+ # Returns +self+.
def toggle(attribute)
self[attribute] = !send("#{attribute}?")
self
end
- # Toggles the +attribute+ and saves the record.
- # Note: Updates made with this method aren't subjected to validation checks
+ # Wrapper around +toggle+ that saves the record. This method differs from
+ # its non-bang version in that it passes through the attribute setter.
+ # Saving is not subjected to validation checks. Returns +true+ if the
+ # record could be saved.
def toggle!(attribute)
toggle(attribute).update_attribute(attribute, self[attribute])
end
@@ -2582,7 +2588,7 @@ module ActiveRecord #:nodoc:
end
def instantiate_time_object(name, values)
- if Time.zone && self.class.time_zone_aware_attributes && !self.class.skip_time_zone_conversion_for_attributes.include?(name.to_sym)
+ if self.class.time_zone_aware_attributes && !self.class.skip_time_zone_conversion_for_attributes.include?(name.to_sym)
Time.zone.local(*values)
else
Time.time_with_datetime_fallback(@@default_timezone, *values)
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
index e6b8e3ae90..2afd6064ad 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
@@ -44,6 +44,12 @@ module ActiveRecord
@query_cache_enabled = old
end
+ # Clears the query cache.
+ #
+ # One reason you may wish to call this method explicitly is between queries
+ # that ask the database to randomize results. Otherwise the cache would see
+ # the same SQL query and repeatedly return the same result each time, silently
+ # undermining the randomness you were expecting.
def clear_query_cache
@query_cache.clear if @query_cache
end
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 b556516572..1594be40e2 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -20,6 +20,10 @@ module ActiveRecord
# def tables(name = nil) end
+ def table_exists?(table_name)
+ tables.include?(table_name.to_s)
+ end
+
# Returns an array of indexes for the given table.
# def indexes(table_name, name = nil) end
@@ -93,8 +97,8 @@ module ActiveRecord
yield table_definition
- if options[:force]
- drop_table(table_name, options) rescue nil
+ if options[:force] && table_exists?(table_name)
+ drop_table(table_name, options)
end
create_sql = "CREATE#{' TEMPORARY' if options[:temporary]} TABLE "
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index 5c7e9f27a5..8c286f64db 100755
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -73,7 +73,7 @@ module ActiveRecord
# REFERENTIAL INTEGRITY ====================================
- # Override to turn off referential integrity while executing +&block+
+ # Override to turn off referential integrity while executing <tt>&block</tt>.
def disable_referential_integrity(&block)
yield
end
@@ -101,7 +101,7 @@ module ActiveRecord
false
end
- # Lazily verify this connection, calling +active?+ only if it hasn't
+ # Lazily verify this connection, calling <tt>active?</tt> only if it hasn't
# been called for +timeout+ seconds.
def verify!(timeout)
now = Time.now.to_i
diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
index e742d60c5f..f00a2c8950 100755
--- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -146,19 +146,19 @@ module ActiveRecord
#
# Options:
#
- # * <tt>:host</tt> -- Defaults to localhost
- # * <tt>:port</tt> -- Defaults to 3306
- # * <tt>:socket</tt> -- Defaults to /tmp/mysql.sock
- # * <tt>:username</tt> -- Defaults to root
- # * <tt>:password</tt> -- Defaults to nothing
- # * <tt>:database</tt> -- The name of the database. No default, must be provided.
- # * <tt>:encoding</tt> -- (Optional) Sets the client encoding by executing "SET NAMES <encoding>" after connection
- # * <tt>:sslkey</tt> -- Necessary to use MySQL with an SSL connection
- # * <tt>:sslcert</tt> -- Necessary to use MySQL with an SSL connection
- # * <tt>:sslcapath</tt> -- Necessary to use MySQL with an SSL connection
- # * <tt>:sslcipher</tt> -- Necessary to use MySQL with an SSL connection
+ # * <tt>:host</tt> - Defaults to "localhost".
+ # * <tt>:port</tt> - Defaults to 3306.
+ # * <tt>:socket</tt> - Defaults to "/tmp/mysql.sock".
+ # * <tt>:username</tt> - Defaults to "root"
+ # * <tt>:password</tt> - Defaults to nothing.
+ # * <tt>:database</tt> - The name of the database. No default, must be provided.
+ # * <tt>:encoding</tt> - (Optional) Sets the client encoding by executing "SET NAMES <encoding>" after connection.
+ # * <tt>:sslkey</tt> - Necessary to use MySQL with an SSL connection.
+ # * <tt>:sslcert</tt> - Necessary to use MySQL with an SSL connection.
+ # * <tt>:sslcapath</tt> - Necessary to use MySQL with an SSL connection.
+ # * <tt>:sslcipher</tt> - Necessary to use MySQL with an SSL connection.
#
- # By default, the MysqlAdapter will consider all columns of type tinyint(1)
+ # By default, the MysqlAdapter will consider all columns of type <tt>tinyint(1)</tt>
# as boolean. If you wish to disable this emulation (which was the default
# behavior in versions 0.13.1 and earlier) you can add the following line
# to your environment.rb file:
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index e3f7969cdf..2ec2d80af4 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -228,15 +228,15 @@ module ActiveRecord
#
# Options:
#
- # * <tt>:host</tt> -- Defaults to localhost
- # * <tt>:port</tt> -- Defaults to 5432
- # * <tt>:username</tt> -- Defaults to nothing
- # * <tt>:password</tt> -- Defaults to nothing
- # * <tt>:database</tt> -- The name of the database. No default, must be provided.
- # * <tt>:schema_search_path</tt> -- An optional schema search path for the connection given as a string of comma-separated schema names. This is backward-compatible with the <tt>:schema_order</tt> option.
- # * <tt>:encoding</tt> -- An optional client encoding that is used in a SET client_encoding TO <encoding> call on the connection.
- # * <tt>:min_messages</tt> -- An optional client min messages that is used in a SET client_min_messages TO <min_messages> call on the connection.
- # * <tt>:allow_concurrency</tt> -- If true, use async query methods so Ruby threads don't deadlock; otherwise, use blocking query methods.
+ # * <tt>:host</tt> - Defaults to "localhost".
+ # * <tt>:port</tt> - Defaults to 5432.
+ # * <tt>:username</tt> - Defaults to nothing.
+ # * <tt>:password</tt> - Defaults to nothing.
+ # * <tt>:database</tt> - The name of the database. No default, must be provided.
+ # * <tt>:schema_search_path</tt> - An optional schema search path for the connection given as a string of comma-separated schema names. This is backward-compatible with the <tt>:schema_order</tt> option.
+ # * <tt>:encoding</tt> - An optional client encoding that is used in a <tt>SET client_encoding TO <encoding></tt> call on the connection.
+ # * <tt>:min_messages</tt> - An optional client min messages that is used in a <tt>SET client_min_messages TO <min_messages></tt> call on the connection.
+ # * <tt>:allow_concurrency</tt> - If true, use async query methods so Ruby threads don't deadlock; otherwise, use blocking query methods.
class PostgreSQLAdapter < AbstractAdapter
# Returns 'PostgreSQL' as adapter name for identification purposes.
def adapter_name
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
index 8fa62c1845..8abbc6d0a4 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
@@ -70,7 +70,7 @@ module ActiveRecord
#
# Options:
#
- # * <tt>:database</tt> -- Path to the database file.
+ # * <tt>:database</tt> - Path to the database file.
class SQLiteAdapter < AbstractAdapter
def adapter_name #:nodoc:
'SQLite'
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index 7d5fd35dae..9367ea523d 100755
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -426,7 +426,7 @@ end
# == Support for YAML defaults
#
# You probably already know how to use YAML to set and reuse defaults in
-# your +database.yml+ file,. You can use the same technique in your fixtures:
+# your <tt>database.yml</tt> file. You can use the same technique in your fixtures:
#
# DEFAULTS: &DEFAULTS
# created_on: <%= 3.weeks.ago.to_s(:db) %>
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index 61005af83f..8614ef8751 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -153,6 +153,17 @@ module ActiveRecord
end
end
+ # Returns the AssociationReflection object specified in the <tt>:through</tt> option
+ # of a HasManyThrough or HasOneThrough association. Example:
+ #
+ # class Post < ActiveRecord::Base
+ # has_many :taggings
+ # has_many :tags, :through => :taggings
+ # end
+ #
+ # tags_reflection = Post.reflect_on_association(:tags)
+ # taggings_reflection = tags_reflection.through_reflection
+ #
def through_reflection
@through_reflection ||= options[:through] ? active_record.reflect_on_association(options[:through]) : false
end
@@ -168,7 +179,8 @@ module ActiveRecord
# Gets the source of the through reflection. It checks both a singularized and pluralized form for <tt>:belongs_to</tt> or <tt>:has_many</tt>.
# (The <tt>:tags</tt> association on Tagging below.)
#
- # class Post
+ # class Post < ActiveRecord::Base
+ # has_many :taggings
# has_many :tags, :through => :taggings
# end
#
diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb
index 50db32725d..d25e8cd0da 100755
--- a/activerecord/lib/active_record/validations.rb
+++ b/activerecord/lib/active_record/validations.rb
@@ -678,7 +678,7 @@ module ActiveRecord
# * <tt>:allow_nil</tt> - If set to true, skips this validation if the attribute is +nil+ (default is +false+).
# * <tt>:allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is +false+).
# * <tt>:with</tt> - The regular expression used to validate the format with (note: must be supplied!).
- # * <tt>:on</tt> Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>).
+ # * <tt>:on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>).
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
# occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
# method, proc or string should return or evaluate to a true or false value.
@@ -784,7 +784,7 @@ module ActiveRecord
#
# Configuration options:
# * <tt>:message</tt> - A custom error message (default is: "is invalid")
- # * <tt>:on</tt> Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>)
+ # * <tt>:on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>).
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
# occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
# method, proc or string should return or evaluate to a true or false value.
@@ -802,8 +802,8 @@ module ActiveRecord
end
# Validates whether the value of the specified attribute is numeric by trying to convert it to
- # a float with Kernel.Float (if <tt>integer</tt> is false) or applying it to the regular expression
- # <tt>/\A[\+\-]?\d+\Z/</tt> (if <tt>integer</tt> is set to true).
+ # a float with Kernel.Float (if <tt>only_integer</tt> is false) or applying it to the regular expression
+ # <tt>/\A[\+\-]?\d+\Z/</tt> (if <tt>only_integer</tt> is set to true).
#
# class Person < ActiveRecord::Base
# validates_numericality_of :value, :on => :create
diff --git a/activerecord/test/cases/adapter_test.rb b/activerecord/test/cases/adapter_test.rb
index 8a74b6d6f5..91504af901 100644
--- a/activerecord/test/cases/adapter_test.rb
+++ b/activerecord/test/cases/adapter_test.rb
@@ -6,15 +6,16 @@ class AdapterTest < ActiveRecord::TestCase
end
def test_tables
- if @connection.respond_to?(:tables)
- tables = @connection.tables
- assert tables.include?("accounts")
- assert tables.include?("authors")
- assert tables.include?("tasks")
- assert tables.include?("topics")
- else
- warn "#{@connection.class} does not respond to #tables"
- end
+ tables = @connection.tables
+ assert tables.include?("accounts")
+ assert tables.include?("authors")
+ assert tables.include?("tasks")
+ assert tables.include?("topics")
+ end
+
+ def test_table_exists?
+ assert @connection.table_exists?("accounts")
+ assert !@connection.table_exists?("nonexistingtable")
end
def test_indexes
diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index 546ed80894..67b57ceb42 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -29,6 +29,10 @@ class EagerAssociationTest < ActiveRecord::TestCase
post = Post.find(:first, :include => :comments, :conditions => "posts.title = 'Welcome to the weblog'")
assert_equal 2, post.comments.size
assert post.comments.include?(comments(:greetings))
+
+ posts = Post.find(:all, :include => :last_comment)
+ post = posts.find { |p| p.id == 1 }
+ assert_equal Post.find(1).last_comment, post.last_comment
end
def test_loading_conditions_with_or
diff --git a/activerecord/test/cases/associations_test.rb b/activerecord/test/cases/associations_test.rb
index ed2fab6d22..d8fe98bf57 100755
--- a/activerecord/test/cases/associations_test.rb
+++ b/activerecord/test/cases/associations_test.rb
@@ -149,6 +149,12 @@ class AssociationProxyTest < ActiveRecord::TestCase
assert !david.projects.loaded?
end
+ def test_inspect_does_not_reload_a_not_yet_loaded_target
+ andreas = Developer.new :name => 'Andreas', :log => 'new developer added'
+ assert !andreas.audit_logs.loaded?
+ assert_match(/message: "new developer added"/, andreas.audit_logs.inspect)
+ end
+
def test_save_on_parent_saves_children
developer = Developer.create :name => "Bryan", :salary => 50_000
assert_equal 1, developer.reload.audit_logs.size
diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb
index 61a049ab36..c336fd9afb 100755
--- a/activerecord/test/cases/attribute_methods_test.rb
+++ b/activerecord/test/cases/attribute_methods_test.rb
@@ -173,6 +173,41 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
+ def test_setting_time_zone_aware_attribute_with_string
+ utc_time = Time.utc(2008, 1, 1)
+ (-11..13).each do |timezone_offset|
+ time_string = utc_time.in_time_zone(timezone_offset).to_s
+ in_time_zone "Pacific Time (US & Canada)" do
+ record = @target.new
+ record.written_on = time_string
+ assert_equal Time.zone.parse(time_string), record.written_on
+ assert_equal TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
+ assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time
+ end
+ end
+ end
+
+ def test_setting_time_zone_aware_attribute_to_blank_string_returns_nil
+ in_time_zone "Pacific Time (US & Canada)" do
+ record = @target.new
+ record.written_on = ' '
+ assert_nil record.written_on
+ end
+ end
+
+ def test_setting_time_zone_aware_attribute_interprets_time_zone_unaware_string_in_time_zone
+ time_string = 'Tue Jan 01 00:00:00 2008'
+ (-11..13).each do |timezone_offset|
+ in_time_zone timezone_offset do
+ record = @target.new
+ record.written_on = time_string
+ assert_equal Time.zone.parse(time_string), record.written_on
+ assert_equal TimeZone[timezone_offset], record.written_on.time_zone
+ assert_equal Time.utc(2008, 1, 1), record.written_on.time
+ end
+ end
+ end
+
def test_setting_time_zone_aware_attribute_in_current_time_zone
utc_time = Time.utc(2008, 1, 1)
in_time_zone "Pacific Time (US & Canada)" do
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index b7f87fe6e8..2acfe9b387 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -8,6 +8,7 @@ require 'models/entrant'
require 'models/developer'
require 'models/post'
require 'models/customer'
+require 'models/job'
class FinderTest < ActiveRecord::TestCase
fixtures :companies, :topics, :entrants, :developers, :developers_projects, :posts, :comments, :accounts, :authors, :customers
@@ -857,6 +858,14 @@ class FinderTest < ActiveRecord::TestCase
Company.connection.select_rows("SELECT id, name FROM companies WHERE id IN (1,2,3) ORDER BY id").map! {|i| i.map! {|j| j.to_s unless j.nil?}}
end
+ def test_find_with_order_on_included_associations_with_construct_finder_sql_for_association_limiting_and_is_distinct
+ assert_equal 2, Post.find(:all,:include=>{:authors=>:author_address},:order=>' author_addresses.id DESC ', :limit=>2).size
+
+ assert_equal 3, Post.find(:all,:include=>{:author=>:author_address,:authors=>:author_address},
+ :order=>' author_addresses_authors.id DESC ', :limit=>3).size
+ end
+
+
protected
def bind(statement, *vars)
if vars.first.is_a?(Hash)
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index d4e81827aa..6be31b5f86 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -209,6 +209,24 @@ if ActiveRecord::Base.connection.supports_migrations?
ActiveRecord::Base.primary_key_prefix_type = nil
end
+ uses_mocha('test_create_table_with_force_true_does_not_drop_nonexisting_table') do
+ def test_create_table_with_force_true_does_not_drop_nonexisting_table
+ if Person.connection.table_exists?(:testings2)
+ Person.connection.drop_table :testings2
+ end
+
+ # using a copy as we need the drop_table method to
+ # continue to work for the ensure block of the test
+ temp_conn = Person.connection.dup
+ temp_conn.expects(:drop_table).never
+ temp_conn.create_table :testings2, :force => true do |t|
+ t.column :foo, :string
+ end
+ ensure
+ Person.connection.drop_table :testings2 rescue nil
+ end
+ end
+
# SQL Server, Sybase, and SQLite3 will not allow you to add a NOT NULL
# column to a table without a default value.
diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb
index ba8bff3b44..c42b0efba0 100644
--- a/activerecord/test/cases/schema_dumper_test.rb
+++ b/activerecord/test/cases/schema_dumper_test.rb
@@ -2,140 +2,137 @@ require "cases/helper"
require 'active_record/schema_dumper'
require 'stringio'
-if ActiveRecord::Base.connection.respond_to?(:tables)
- class SchemaDumperTest < ActiveRecord::TestCase
- def standard_dump
- stream = StringIO.new
- ActiveRecord::SchemaDumper.ignore_tables = []
- ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
- stream.string
- end
+class SchemaDumperTest < ActiveRecord::TestCase
+ def standard_dump
+ stream = StringIO.new
+ ActiveRecord::SchemaDumper.ignore_tables = []
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
+ stream.string
+ end
- def test_schema_dump
- output = standard_dump
- assert_match %r{create_table "accounts"}, output
- assert_match %r{create_table "authors"}, output
- assert_no_match %r{create_table "schema_migrations"}, output
- end
+ def test_schema_dump
+ output = standard_dump
+ assert_match %r{create_table "accounts"}, output
+ assert_match %r{create_table "authors"}, output
+ assert_no_match %r{create_table "schema_migrations"}, output
+ end
- def test_schema_dump_excludes_sqlite_sequence
- output = standard_dump
- assert_no_match %r{create_table "sqlite_sequence"}, output
- end
+ def test_schema_dump_excludes_sqlite_sequence
+ output = standard_dump
+ assert_no_match %r{create_table "sqlite_sequence"}, output
+ end
- def assert_line_up(lines, pattern, required = false)
- return assert(true) if lines.empty?
- matches = lines.map { |line| line.match(pattern) }
- assert matches.all? if required
- matches.compact!
- return assert(true) if matches.empty?
- assert_equal 1, matches.map{ |match| match.offset(0).first }.uniq.length
- end
+ def assert_line_up(lines, pattern, required = false)
+ return assert(true) if lines.empty?
+ matches = lines.map { |line| line.match(pattern) }
+ assert matches.all? if required
+ matches.compact!
+ return assert(true) if matches.empty?
+ assert_equal 1, matches.map{ |match| match.offset(0).first }.uniq.length
+ end
- def column_definition_lines(output = standard_dump)
- output.scan(/^( *)create_table.*?\n(.*?)^\1end/m).map{ |m| m.last.split(/\n/) }
- end
+ def column_definition_lines(output = standard_dump)
+ output.scan(/^( *)create_table.*?\n(.*?)^\1end/m).map{ |m| m.last.split(/\n/) }
+ end
- def test_types_line_up
- column_definition_lines.each do |column_set|
- next if column_set.empty?
+ def test_types_line_up
+ column_definition_lines.each do |column_set|
+ next if column_set.empty?
- lengths = column_set.map do |column|
- if match = column.match(/t\.(?:integer|decimal|float|datetime|timestamp|time|date|text|binary|string|boolean)\s+"/)
- match[0].length
- end
+ lengths = column_set.map do |column|
+ if match = column.match(/t\.(?:integer|decimal|float|datetime|timestamp|time|date|text|binary|string|boolean)\s+"/)
+ match[0].length
end
-
- assert_equal 1, lengths.uniq.length
end
- end
- def test_arguments_line_up
- column_definition_lines.each do |column_set|
- assert_line_up(column_set, /:default => /)
- assert_line_up(column_set, /:limit => /)
- assert_line_up(column_set, /:null => /)
- end
+ assert_equal 1, lengths.uniq.length
end
+ end
- def test_no_dump_errors
- output = standard_dump
- assert_no_match %r{\# Could not dump table}, output
+ def test_arguments_line_up
+ column_definition_lines.each do |column_set|
+ assert_line_up(column_set, /:default => /)
+ assert_line_up(column_set, /:limit => /)
+ assert_line_up(column_set, /:null => /)
end
+ end
- def test_schema_dump_includes_not_null_columns
- stream = StringIO.new
+ def test_no_dump_errors
+ output = standard_dump
+ assert_no_match %r{\# Could not dump table}, output
+ end
- ActiveRecord::SchemaDumper.ignore_tables = [/^[^r]/]
- ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
- output = stream.string
- assert_match %r{:null => false}, output
- end
+ def test_schema_dump_includes_not_null_columns
+ stream = StringIO.new
- def test_schema_dump_with_string_ignored_table
- stream = StringIO.new
+ ActiveRecord::SchemaDumper.ignore_tables = [/^[^r]/]
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
+ output = stream.string
+ assert_match %r{:null => false}, output
+ end
- ActiveRecord::SchemaDumper.ignore_tables = ['accounts']
- ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
- output = stream.string
- assert_no_match %r{create_table "accounts"}, output
- assert_match %r{create_table "authors"}, output
- assert_no_match %r{create_table "schema_migrations"}, output
- end
+ def test_schema_dump_with_string_ignored_table
+ stream = StringIO.new
+
+ ActiveRecord::SchemaDumper.ignore_tables = ['accounts']
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
+ output = stream.string
+ assert_no_match %r{create_table "accounts"}, output
+ assert_match %r{create_table "authors"}, output
+ assert_no_match %r{create_table "schema_migrations"}, output
+ end
+
+ def test_schema_dump_with_regexp_ignored_table
+ stream = StringIO.new
- def test_schema_dump_with_regexp_ignored_table
- stream = StringIO.new
+ ActiveRecord::SchemaDumper.ignore_tables = [/^account/]
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
+ output = stream.string
+ assert_no_match %r{create_table "accounts"}, output
+ assert_match %r{create_table "authors"}, output
+ assert_no_match %r{create_table "schema_migrations"}, output
+ end
- ActiveRecord::SchemaDumper.ignore_tables = [/^account/]
+ def test_schema_dump_illegal_ignored_table_value
+ stream = StringIO.new
+ ActiveRecord::SchemaDumper.ignore_tables = [5]
+ assert_raise(StandardError) do
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
- output = stream.string
- assert_no_match %r{create_table "accounts"}, output
- assert_match %r{create_table "authors"}, output
- assert_no_match %r{create_table "schema_migrations"}, output
end
+ end
- def test_schema_dump_illegal_ignored_table_value
- stream = StringIO.new
- ActiveRecord::SchemaDumper.ignore_tables = [5]
- assert_raise(StandardError) do
- ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
- end
+ if current_adapter?(:MysqlAdapter)
+ def test_schema_dump_should_not_add_default_value_for_mysql_text_field
+ output = standard_dump
+ assert_match %r{t.text\s+"body",\s+:null => false$}, output
end
- if current_adapter?(:MysqlAdapter)
- def test_schema_dump_should_not_add_default_value_for_mysql_text_field
- output = standard_dump
- assert_match %r{t.text\s+"body",\s+:null => false$}, output
- end
-
- def test_mysql_schema_dump_should_honor_nonstandard_primary_keys
- output = standard_dump
- match = output.match(%r{create_table "movies"(.*)do})
- assert_not_nil(match, "nonstandardpk table not found")
- assert_match %r(:primary_key => "movieid"), match[1], "non-standard primary key not preserved"
- end
-
- def test_schema_dump_includes_length_for_mysql_blob_and_text_fields
- output = standard_dump
- assert_match %r{t.binary\s+"tiny_blob",\s+:limit => 255$}, output
- assert_match %r{t.binary\s+"normal_blob"$}, output
- assert_match %r{t.binary\s+"medium_blob",\s+:limit => 16777215$}, output
- assert_match %r{t.binary\s+"long_blob",\s+:limit => 2147483647$}, output
- assert_match %r{t.text\s+"tiny_text",\s+:limit => 255$}, output
- assert_match %r{t.text\s+"normal_text"$}, output
- assert_match %r{t.text\s+"medium_text",\s+:limit => 16777215$}, output
- assert_match %r{t.text\s+"long_text",\s+:limit => 2147483647$}, output
- end
+ def test_mysql_schema_dump_should_honor_nonstandard_primary_keys
+ output = standard_dump
+ match = output.match(%r{create_table "movies"(.*)do})
+ assert_not_nil(match, "nonstandardpk table not found")
+ assert_match %r(:primary_key => "movieid"), match[1], "non-standard primary key not preserved"
end
- def test_schema_dump_includes_decimal_options
- stream = StringIO.new
- ActiveRecord::SchemaDumper.ignore_tables = [/^[^n]/]
- ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
- output = stream.string
- assert_match %r{:precision => 3,[[:space:]]+:scale => 2,[[:space:]]+:default => 2.78}, output
+ def test_schema_dump_includes_length_for_mysql_blob_and_text_fields
+ output = standard_dump
+ assert_match %r{t.binary\s+"tiny_blob",\s+:limit => 255$}, output
+ assert_match %r{t.binary\s+"normal_blob"$}, output
+ assert_match %r{t.binary\s+"medium_blob",\s+:limit => 16777215$}, output
+ assert_match %r{t.binary\s+"long_blob",\s+:limit => 2147483647$}, output
+ assert_match %r{t.text\s+"tiny_text",\s+:limit => 255$}, output
+ assert_match %r{t.text\s+"normal_text"$}, output
+ assert_match %r{t.text\s+"medium_text",\s+:limit => 16777215$}, output
+ assert_match %r{t.text\s+"long_text",\s+:limit => 2147483647$}, output
end
end
+ def test_schema_dump_includes_decimal_options
+ stream = StringIO.new
+ ActiveRecord::SchemaDumper.ignore_tables = [/^[^n]/]
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
+ output = stream.string
+ assert_match %r{:precision => 3,[[:space:]]+:scale => 2,[[:space:]]+:default => 2.78}, output
+ end
end
diff --git a/activerecord/test/models/developer.rb b/activerecord/test/models/developer.rb
index 192c2cb5ab..f77fd0e96d 100644
--- a/activerecord/test/models/developer.rb
+++ b/activerecord/test/models/developer.rb
@@ -49,6 +49,10 @@ class Developer < ActiveRecord::Base
before_create do |developer|
developer.audit_logs.build :message => "Computer created"
end
+
+ def log=(message)
+ audit_logs.build :message => message
+ end
end
class AuditLog < ActiveRecord::Base
diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb
index 22c5a645b8..d9101706b5 100644
--- a/activerecord/test/models/post.rb
+++ b/activerecord/test/models/post.rb
@@ -9,6 +9,8 @@ class Post < ActiveRecord::Base
belongs_to :author_with_posts, :class_name => "Author", :foreign_key => :author_id, :include => :posts
+ has_one :last_comment, :class_name => 'Comment', :order => 'id desc'
+
has_many :comments, :order => "body" do
def find_most_recent
find(:first, :order => "id DESC")
diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb
index eff92c91de..08fd0123f6 100644
--- a/activeresource/lib/active_resource/base.rb
+++ b/activeresource/lib/active_resource/base.rb
@@ -14,12 +14,19 @@ module ActiveResource
# Person maps to the resources people, very similarly to Active Record) and a +site+ value, which holds the
# URI of the resources.
#
- # class Person < ActiveResource::Base
- # self.site = "http://api.people.com:3000/"
- # end
+ # class Person < ActiveResource::Base
+ # self.site = "http://api.people.com:3000/"
+ # end
#
# Now the Person class is mapped to RESTful resources located at <tt>http://api.people.com:3000/people/</tt>, and
- # you can now use Active Resource's lifecycles methods to manipulate resources.
+ # you can now use Active Resource's lifecycles methods to manipulate resources. In the case where you already have
+ # an existing model with the same name as the desired RESTful resource you can set the +element_name+ value.
+ #
+ # class PersonResource < ActiveResource::Base
+ # self.site = "http://api.people.com:3000/"
+ # self.element_name = "person"
+ # end
+ #
#
# == Lifecycle methods
#
@@ -72,13 +79,13 @@ module ActiveResource
#
# You can validate resources client side by overriding validation methods in the base class.
#
- # class Person < ActiveResource::Base
- # self.site = "http://api.people.com:3000/"
- # protected
- # def validate
- # errors.add("last", "has invalid characters") unless last =~ /[a-zA-Z]*/
- # end
- # end
+ # class Person < ActiveResource::Base
+ # self.site = "http://api.people.com:3000/"
+ # protected
+ # def validate
+ # errors.add("last", "has invalid characters") unless last =~ /[a-zA-Z]*/
+ # end
+ # end
#
# See the ActiveResource::Validations documentation for more information.
#
@@ -118,18 +125,17 @@ module ActiveResource
# exception.
#
# # GET http://api.people.com:3000/people/999.xml
- # ryan = Person.find(999) # => Raises ActiveResource::ResourceNotFound
- # # => Response = 404
+ # ryan = Person.find(999) # 404, raises ActiveResource::ResourceNotFound
#
# <tt>404</tt> is just one of the HTTP error response codes that ActiveResource will handle with its own exception. The
# following HTTP response codes will also result in these exceptions:
#
- # 200 - 399:: Valid response, no exception
- # 404:: ActiveResource::ResourceNotFound
- # 409:: ActiveResource::ResourceConflict
- # 422:: ActiveResource::ResourceInvalid (rescued by save as validation errors)
- # 401 - 499:: ActiveResource::ClientError
- # 500 - 599:: ActiveResource::ServerError
+ # * 200..399 - Valid response, no exception
+ # * 404 - ActiveResource::ResourceNotFound
+ # * 409 - ActiveResource::ResourceConflict
+ # * 422 - ActiveResource::ResourceInvalid (rescued by save as validation errors)
+ # * 401..499 - ActiveResource::ClientError
+ # * 500..599 - ActiveResource::ServerError
#
# These custom exceptions allow you to deal with resource errors more naturally and with more precision
# rather than returning a general HTTP error. For example:
@@ -177,12 +183,15 @@ module ActiveResource
# self.timeout = 5
# end
#
- # This sets the +timeout+ to 5 seconds. You can adjust the timeout to a value suitable for the RESTful API
+ # This sets the +timeout+ to 5 seconds. You can adjust the +timeout+ to a value suitable for the RESTful API
# you are accessing. It is recommended to set this to a reasonably low value to allow your Active Resource
# clients (especially if you are using Active Resource in a Rails application) to fail-fast (see
# http://en.wikipedia.org/wiki/Fail-fast) rather than cause cascading failures that could incapacitate your
# server.
#
+ # When a timeout occurs, an ActiveResource::TimeoutError is raised. You should rescue from
+ # ActiveResource::TimeoutError in your Active Resource method calls.
+ #
# Internally, Active Resource relies on Ruby's Net::HTTP library to make HTTP requests. Setting +timeout+
# sets the <tt>read_timeout</tt> of the internal Net::HTTP instance to the same value. The default
# <tt>read_timeout</tt> is 60 seconds on most Ruby implementations.
@@ -229,7 +238,7 @@ module ActiveResource
end
end
- # Gets the user for REST HTTP authentication
+ # Gets the user for REST HTTP authentication.
def user
# Not using superclass_delegating_reader. See +site+ for explanation
if defined?(@user)
@@ -239,13 +248,13 @@ module ActiveResource
end
end
- # Sets the user for REST HTTP authentication
+ # Sets the user for REST HTTP authentication.
def user=(user)
@connection = nil
@user = user
end
- # Gets the password for REST HTTP authentication
+ # Gets the password for REST HTTP authentication.
def password
# Not using superclass_delegating_reader. See +site+ for explanation
if defined?(@password)
@@ -255,13 +264,13 @@ module ActiveResource
end
end
- # Sets the password for REST HTTP authentication
+ # Sets the password for REST HTTP authentication.
def password=(password)
@connection = nil
@password = password
end
- # Sets the format that attributes are sent and received in from a mime type reference. Example:
+ # Sets the format that attributes are sent and received in from a mime type reference:
#
# Person.format = :json
# Person.find(1) # => GET /people/1.json
@@ -269,7 +278,7 @@ module ActiveResource
# Person.format = ActiveResource::Formats::XmlFormat
# Person.find(1) # => GET /people/1.xml
#
- # Default format is :xml.
+ # Default format is <tt>:xml</tt>.
def format=(mime_type_reference_or_format)
format = mime_type_reference_or_format.is_a?(Symbol) ?
ActiveResource::Formats[mime_type_reference_or_format] : mime_type_reference_or_format
@@ -278,7 +287,7 @@ module ActiveResource
connection.format = format if site
end
- # Returns the current format, default is ActiveResource::Formats::XmlFormat
+ # Returns the current format, default is ActiveResource::Formats::XmlFormat.
def format # :nodoc:
read_inheritable_attribute("format") || ActiveResource::Formats[:xml]
end
@@ -367,9 +376,9 @@ module ActiveResource
# will split from the prefix options.
#
# ==== Options
- # +prefix_options+:: A hash to add a prefix to the request for nested URL's (e.g., <tt>:account_id => 19</tt>
+ # +prefix_options+ - A hash to add a prefix to the request for nested URLs (e.g., <tt>:account_id => 19</tt>
# would yield a URL like <tt>/accounts/19/purchases.xml</tt>).
- # +query_options+:: A hash to add items to the query string for the request.
+ # +query_options+ - A hash to add items to the query string for the request.
#
# ==== Examples
# Post.element_path(1)
@@ -393,9 +402,9 @@ module ActiveResource
# will split from the +prefix_options+.
#
# ==== Options
- # +prefix_options+:: A hash to add a prefix to the request for nested URL's (e.g., <tt>:account_id => 19</tt>
- # would yield a URL like <tt>/accounts/19/purchases.xml</tt>).
- # +query_options+:: A hash to add items to the query string for the request.
+ # * +prefix_options+ - A hash to add a prefix to the request for nested URL's (e.g., <tt>:account_id => 19</tt>
+ # would yield a URL like <tt>/accounts/19/purchases.xml</tt>).
+ # * +query_options+ - A hash to add items to the query string for the request.
#
# ==== Examples
# Post.collection_path
@@ -431,40 +440,34 @@ module ActiveResource
# ==== Examples
# Person.create(:name => 'Jeremy', :email => 'myname@nospam.com', :enabled => true)
# my_person = Person.find(:first)
- # my_person.email
- # # => myname@nospam.com
+ # my_person.email # => myname@nospam.com
#
# dhh = Person.create(:name => 'David', :email => 'dhh@nospam.com', :enabled => true)
- # dhh.valid?
- # # => true
- # dhh.new?
- # # => false
+ # dhh.valid? # => true
+ # dhh.new? # => false
#
# # We'll assume that there's a validation that requires the name attribute
# that_guy = Person.create(:name => '', :email => 'thatguy@nospam.com', :enabled => true)
- # that_guy.valid?
- # # => false
- # that_guy.new?
- # # => true
- #
+ # that_guy.valid? # => false
+ # that_guy.new? # => true
def create(attributes = {})
returning(self.new(attributes)) { |res| res.save }
end
- # Core method for finding resources. Used similarly to Active Record's find method.
+ # Core method for finding resources. Used similarly to Active Record's +find+ method.
#
# ==== Arguments
# The first argument is considered to be the scope of the query. That is, how many
# resources are returned from the request. It can be one of the following.
#
- # * <tt>:one</tt>: Returns a single resource.
- # * <tt>:first</tt>: Returns the first resource found.
- # * <tt>:all</tt>: Returns every resource that matches the request.
+ # * <tt>:one</tt> - Returns a single resource.
+ # * <tt>:first</tt> - Returns the first resource found.
+ # * <tt>:all</tt> - Returns every resource that matches the request.
#
# ==== Options
#
- # * +from+: Sets the path or custom method that resources will be fetched from.
- # * +params+: Sets query and prefix (nested URL) parameters.
+ # * <tt>:from</tt> - Sets the path or custom method that resources will be fetched from.
+ # * <tt>:params</tt> - Sets query and prefix (nested URL) parameters.
#
# ==== Examples
# Person.find(1)
@@ -511,19 +514,14 @@ module ActiveResource
# All options specify prefix and query parameters.
#
# ==== Examples
- # Event.delete(2)
- # # => DELETE /events/2
+ # Event.delete(2) # sends DELETE /events/2
#
# Event.create(:name => 'Free Concert', :location => 'Community Center')
- # my_event = Event.find(:first)
- # # => Events (id: 7)
- # Event.delete(my_event.id)
- # # => DELETE /events/7
+ # my_event = Event.find(:first) # let's assume this is event with ID 7
+ # Event.delete(my_event.id) # sends DELETE /events/7
#
# # Let's assume a request to events/5/cancel.xml
- # Event.delete(params[:id])
- # # => DELETE /events/5
- #
+ # Event.delete(params[:id]) # sends DELETE /events/5
def delete(id, options = {})
connection.delete(element_path(id, options))
end
@@ -532,11 +530,9 @@ module ActiveResource
#
# ==== Examples
# Note.create(:title => 'Hello, world.', :body => 'Nothing more for now...')
- # Note.exists?(1)
- # # => true
+ # Note.exists?(1) # => true
#
- # Note.exists(1349)
- # # => false
+ # Note.exists(1349) # => false
def exists?(id, options = {})
if id
prefix_options, query_options = split_options(options[:params])
@@ -626,7 +622,7 @@ module ActiveResource
attr_accessor :attributes #:nodoc:
attr_accessor :prefix_options #:nodoc:
- # Constructor method for new resources; the optional +attributes+ parameter takes a +Hash+
+ # Constructor method for new resources; the optional +attributes+ parameter takes a hash
# of attributes for the new resource.
#
# ==== Examples
@@ -643,27 +639,26 @@ module ActiveResource
load(attributes)
end
- # Returns a clone of the resource that hasn't been assigned an id yet and
+ # Returns a clone of the resource that hasn't been assigned an +id+ yet and
# is treated as a new resource.
#
- # ryan = Person.find(1)
- # not_ryan = ryan.clone
- # not_ryan.new? # => true
+ # ryan = Person.find(1)
+ # not_ryan = ryan.clone
+ # not_ryan.new? # => true
#
# Any active resource member attributes will NOT be cloned, though all other
- # attributes are. This is to prevent the conflict between any prefix_options
+ # attributes are. This is to prevent the conflict between any +prefix_options+
# that refer to the original parent resource and the newly cloned parent
# resource that does not exist.
#
- # ryan = Person.find(1)
- # ryan.address = StreetAddress.find(1, :person_id => ryan.id)
- # ryan.hash = {:not => "an ARes instance"}
- #
- # not_ryan = ryan.clone
- # not_ryan.new? # => true
- # not_ryan.address # => NoMethodError
- # not_ryan.hash # => {:not => "an ARes instance"}
- #
+ # ryan = Person.find(1)
+ # ryan.address = StreetAddress.find(1, :person_id => ryan.id)
+ # ryan.hash = {:not => "an ARes instance"}
+ #
+ # not_ryan = ryan.clone
+ # not_ryan.new? # => true
+ # not_ryan.address # => NoMethodError
+ # not_ryan.hash # => {:not => "an ARes instance"}
def clone
# Clone all attributes except the pk and any nested ARes
cloned = attributes.reject {|k,v| k == self.class.primary_key || v.is_a?(ActiveResource::Base)}.inject({}) do |attrs, (k, v)|
@@ -684,16 +679,13 @@ module ActiveResource
#
# ==== Examples
# not_new = Computer.create(:brand => 'Apple', :make => 'MacBook', :vendor => 'MacMall')
- # not_new.new?
- # # => false
+ # not_new.new? # => false
#
# is_new = Computer.new(:brand => 'IBM', :make => 'Thinkpad', :vendor => 'IBM')
- # is_new.new?
- # # => true
+ # is_new.new? # => true
#
# is_new.save
- # is_new.new?
- # # => false
+ # is_new.new? # => false
#
def new?
id.nil?
@@ -715,7 +707,7 @@ module ActiveResource
end
# Test for equality. Resource are equal if and only if +other+ is the same object or
- # is an instance of the same class, is not +new?+, and has the same +id+.
+ # is an instance of the same class, is not <tt>new?</tt>, and has the same +id+.
#
# ==== Examples
# ryan = Person.create(:name => 'Ryan')
@@ -756,17 +748,13 @@ module ActiveResource
# ==== Examples
# my_invoice = Invoice.create(:customer => 'That Company')
# next_invoice = my_invoice.dup
- # next_invoice.new?
- # # => true
+ # next_invoice.new? # => true
#
# next_invoice.save
- # next_invoice == my_invoice
- # # => false (different id attributes)
+ # next_invoice == my_invoice # => false (different id attributes)
#
- # my_invoice.customer
- # # => That Company
- # next_invoice.customer
- # # => That Company
+ # my_invoice.customer # => That Company
+ # next_invoice.customer # => That Company
def dup
returning self.class.new do |resource|
resource.attributes = @attributes
@@ -781,16 +769,12 @@ module ActiveResource
#
# ==== Examples
# my_company = Company.new(:name => 'RoleModel Software', :owner => 'Ken Auer', :size => 2)
- # my_company.new?
- # # => true
- # my_company.save
- # # => POST /companies/ (create)
+ # my_company.new? # => true
+ # my_company.save # sends POST /companies/ (create)
#
- # my_company.new?
- # # => false
+ # my_company.new? # => false
# my_company.size = 10
- # my_company.save
- # # => PUT /companies/1 (update)
+ # my_company.save # sends PUT /companies/1 (update)
def save
new? ? create : update
end
@@ -801,20 +785,17 @@ module ActiveResource
# my_id = 3
# my_person = Person.find(my_id)
# my_person.destroy
- # Person.find(my_id)
- # # => 404 (Resource Not Found)
+ # Person.find(my_id) # 404 (Resource Not Found)
#
# new_person = Person.create(:name => 'James')
- # new_id = new_person.id
- # # => 7
+ # new_id = new_person.id # => 7
# new_person.destroy
- # Person.find(new_id)
- # # => 404 (Resource Not Found)
+ # Person.find(new_id) # 404 (Resource Not Found)
def destroy
connection.delete(element_path, self.class.headers)
end
- # Evaluates to <tt>true</tt> if this resource is not +new?+ and is
+ # Evaluates to <tt>true</tt> if this resource is not <tt>new?</tt> and is
# found on the remote service. Using this method, you can check for
# resources that may have been deleted between the object's instantiation
# and actions on it.
@@ -822,17 +803,14 @@ module ActiveResource
# ==== Examples
# Person.create(:name => 'Theodore Roosevelt')
# that_guy = Person.find(:first)
- # that_guy.exists?
- # # => true
+ # that_guy.exists? # => true
#
# that_lady = Person.new(:name => 'Paul Bean')
- # that_lady.exists?
- # # => false
+ # that_lady.exists? # => false
#
# guys_id = that_guy.id
# Person.delete(guys_id)
- # that_guy.exists?
- # # => false
+ # that_guy.exists? # => false
def exists?
!new? && self.class.exists?(to_param, :params => prefix_options)
end
@@ -844,11 +822,11 @@ module ActiveResource
# attribute, so it has the same options as the +to_xml+ methods in
# ActiveSupport.
#
- # indent:: Set the indent level for the XML output (default is +2+).
- # dasherize:: Boolean option to determine whether or not element names should
- # replace underscores with dashes (default is <tt>false</tt>).
- # skip_instruct:: Toggle skipping the +instruct!+ call on the XML builder
- # that generates the XML declaration (default is <tt>false</tt>).
+ # * <tt>:indent</tt> - Set the indent level for the XML output (default is +2+).
+ # * <tt>:dasherize</tt> - Boolean option to determine whether or not element names should
+ # replace underscores with dashes (default is <tt>false</tt>).
+ # * <tt>:skip_instruct</tt> - Toggle skipping the +instruct!+ call on the XML builder
+ # that generates the XML declaration (default is <tt>false</tt>).
#
# ==== Examples
# my_group = SubsidiaryGroup.find(:first)
@@ -870,30 +848,26 @@ module ActiveResource
#
# ==== Examples
# my_branch = Branch.find(:first)
- # my_branch.name
- # # => Wislon Raod
+ # my_branch.name # => "Wislon Raod"
#
# # Another client fixes the typo...
#
- # my_branch.name
- # # => Wislon Raod
+ # my_branch.name # => "Wislon Raod"
# my_branch.reload
- # my_branch.name
- # # => Wilson Road
+ # my_branch.name # => "Wilson Road"
def reload
self.load(self.class.find(to_param, :params => @prefix_options).attributes)
end
# A method to manually load attributes from a hash. Recursively loads collections of
- # resources. This method is called in initialize and create when a +Hash+ of attributes
+ # resources. This method is called in +initialize+ and +create+ when a hash of attributes
# is provided.
#
# ==== Examples
# my_attrs = {:name => 'J&J Textiles', :industry => 'Cloth and textiles'}
#
# the_supplier = Supplier.find(:first)
- # the_supplier.name
- # # => 'J&M Textiles'
+ # the_supplier.name # => 'J&M Textiles'
# the_supplier.load(my_attrs)
# the_supplier.name('J&J Textiles')
#
@@ -924,10 +898,10 @@ module ActiveResource
self
end
- # For checking respond_to? without searching the attributes (which is faster).
+ # For checking <tt>respond_to?</tt> without searching the attributes (which is faster).
alias_method :respond_to_without_attributes?, :respond_to?
- # A method to determine if an object responds to a message (e.g., a method call). In Active Resource, a +Person+ object with a
+ # A method to determine if an object responds to a message (e.g., a method call). In Active Resource, a Person object with a
# +name+ attribute can answer <tt>true</tt> to <tt>my_person.respond_to?("name")</tt>, <tt>my_person.respond_to?("name=")</tt>, and
# <tt>my_person.respond_to?("name?")</tt>.
def respond_to?(method, include_priv = false)
diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG
index 3ee9ed925c..f72825731e 100644
--- a/activesupport/CHANGELOG
+++ b/activesupport/CHANGELOG
@@ -1,5 +1,13 @@
*SVN*
+* Adding Date.current, which returns Time.zone.today if config.time_zone is set; otherwise returns Date.today [Geoff Buesing]
+
+* TimeWithZone: date part getter methods (#year #mon #day etc) are defined on class; no longer relying on method_missing [Geoff Buesing]
+
+* Time.zone.parse return nil for strings with no date information [Geoff Buesing]
+
+* Time.zone.parse respects offset information in string. Resolves #105. [Scott Fleckenstein, Geoff Buesing]
+
* Added Ruby 1.8 implementation of Process.daemon
* Duration #since and #ago with no argument (e.g., 5.days.ago) return TimeWithZone when config.time_zone is set. Introducing Time.current, which returns Time.zone.now if config.time_zone is set, otherwise just returns Time.now [Geoff Buesing]
diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb
index 183471706b..1e2dbf118e 100644
--- a/activesupport/lib/active_support/core_ext/date/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/date/calculations.rb
@@ -25,6 +25,11 @@ module ActiveSupport #:nodoc:
def tomorrow
::Date.today.tomorrow
end
+
+ # Returns Time.zone.today when config.time_zone is set, otherwise just returns Date.today.
+ def current
+ ::Time.zone_default ? ::Time.zone.today : ::Date.today
+ end
end
# Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00)
diff --git a/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb b/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb
index 2213b09144..c96c5160b3 100644
--- a/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb
+++ b/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb
@@ -23,10 +23,7 @@ class HashWithIndifferentAccess < Hash
alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
alias_method :regular_update, :update unless method_defined?(:regular_update)
- #
- # Assigns a new value to the hash.
- #
- # Example:
+ # Assigns a new value to the hash:
#
# hash = HashWithIndifferentAccess.new
# hash[:key] = "value"
@@ -35,25 +32,15 @@ class HashWithIndifferentAccess < Hash
regular_writer(convert_key(key), convert_value(value))
end
+ # Updates the instantized hash with values from the second:
#
- # Updates the instantized hash with values from the second.
- #
- # Example:
- #
- # >> hash_1 = HashWithIndifferentAccess.new
- # => {}
- #
- # >> hash_1[:key] = "value"
- # => "value"
- #
- # >> hash_2 = HashWithIndifferentAccess.new
- # => {}
+ # hash_1 = HashWithIndifferentAccess.new
+ # hash_1[:key] = "value"
#
- # >> hash_2[:key] = "New Value!"
- # => "New Value!"
+ # hash_2 = HashWithIndifferentAccess.new
+ # hash_2[:key] = "New Value!"
#
- # >> hash_1.update(hash_2)
- # => {"key"=>"New Value!"}
+ # hash_1.update(hash_2) # => {"key"=>"New Value!"}
#
def update(other_hash)
other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
@@ -62,7 +49,13 @@ class HashWithIndifferentAccess < Hash
alias_method :merge!, :update
- # Checks the hash for a key matching the argument passed in
+ # Checks the hash for a key matching the argument passed in:
+ #
+ # hash = HashWithIndifferentAccess.new
+ # hash["key"] = "value"
+ # hash.key? :key # => true
+ # hash.key? "key" # => true
+ #
def key?(key)
super(convert_key(key))
end
@@ -76,7 +69,13 @@ class HashWithIndifferentAccess < Hash
super(convert_key(key), *extras)
end
- # Returns an array of the values at the specified indicies.
+ # Returns an array of the values at the specified indices:
+ #
+ # hash = HashWithIndifferentAccess.new
+ # hash[:a] = "x"
+ # hash[:b] = "y"
+ # hash.values_at("a", "b") # => ["x", "y"]
+ #
def values_at(*indices)
indices.collect {|key| self[convert_key(key)]}
end
diff --git a/activesupport/lib/active_support/deprecation.rb b/activesupport/lib/active_support/deprecation.rb
index c9f1884da3..7613652c71 100644
--- a/activesupport/lib/active_support/deprecation.rb
+++ b/activesupport/lib/active_support/deprecation.rb
@@ -175,6 +175,20 @@ module ActiveSupport
ActiveSupport::Deprecation.warn("#{@var} is deprecated! Call #{@method}.#{called} instead of #{@var}.#{called}. Args: #{args.inspect}", callstack)
end
end
+
+ class DeprecatedInstanceVariable < Delegator #:nodoc:
+ def initialize(value, method)
+ super(value)
+ @method = method
+ @value = value
+ end
+
+ def __getobj__
+ ActiveSupport::Deprecation.warn("Instance variable @#{@method} is deprecated! Call instance method #{@method} instead.")
+ @value
+ end
+ end
+
end
end
diff --git a/activesupport/lib/active_support/inflector.rb b/activesupport/lib/active_support/inflector.rb
index c8736549f4..68fbf3da35 100644
--- a/activesupport/lib/active_support/inflector.rb
+++ b/activesupport/lib/active_support/inflector.rb
@@ -3,6 +3,11 @@ require 'singleton'
# The Inflector transforms words from singular to plural, class names to table names, modularized class names to ones without,
# and class names to foreign keys. The default inflections for pluralization, singularization, and uncountable words are kept
# in inflections.rb.
+#
+# The Rails core team has stated patches for the inflections library will not be accepted
+# in order to avoid breaking legacy applications which may be relying on errant inflections.
+# If you discover an incorrect inflection and require it for your application, you'll need
+# to correct it yourself (explained below).
module Inflector
# A singleton instance of this class is yielded by Inflector.inflections, which can then be used to specify additional
# inflection rules. Examples:
diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb
index 461d52e40e..21ddcaad48 100644
--- a/activesupport/lib/active_support/time_with_zone.rb
+++ b/activesupport/lib/active_support/time_with_zone.rb
@@ -163,6 +163,14 @@ module ActiveSupport
utc.advance(options).in_time_zone(time_zone)
end
+ %w(year mon month day mday hour min sec).each do |method_name|
+ class_eval <<-EOV
+ def #{method_name}
+ time.#{method_name}
+ end
+ EOV
+ end
+
def usec
time.respond_to?(:usec) ? time.usec : 0
end
diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb
index 9cdc2a74ed..0fa99135e2 100644
--- a/activesupport/lib/active_support/values/time_zone.rb
+++ b/activesupport/lib/active_support/values/time_zone.rb
@@ -213,8 +213,14 @@ class TimeZone
# Time.zone.now # => Fri, 31 Dec 1999 14:00:00 HST -10:00
# Time.zone.parse('22:30:00') # => Fri, 31 Dec 1999 22:30:00 HST -10:00
def parse(str, now=now)
+ date_parts = Date._parse(str)
+ return if date_parts.blank?
time = Time.parse(str, now) rescue DateTime.parse(str)
- ActiveSupport::TimeWithZone.new(nil, self, time)
+ if date_parts[:offset].nil?
+ ActiveSupport::TimeWithZone.new(nil, self, time)
+ else
+ time.in_time_zone(self)
+ end
end
# Returns an ActiveSupport::TimeWithZone instance representing the current time
diff --git a/activesupport/test/core_ext/date_ext_test.rb b/activesupport/test/core_ext/date_ext_test.rb
index 2e363c439a..5925ae3a61 100644
--- a/activesupport/test/core_ext/date_ext_test.rb
+++ b/activesupport/test/core_ext/date_ext_test.rb
@@ -208,6 +208,29 @@ class DateExtCalculationsTest < Test::Unit::TestCase
end
end
end
+
+ uses_mocha 'TestDateCurrent' do
+ def test_current_returns_date_today_when_zone_default_not_set
+ with_env_tz 'US/Central' do
+ Time.stubs(:now).returns Time.local(1999, 12, 31, 23)
+ assert_equal Date.new(1999, 12, 31), Date.today
+ assert_equal Date.new(1999, 12, 31), Date.current
+ end
+ end
+
+ def test_current_returns_time_zone_today_when_zone_default_set
+ silence_warnings do # silence warnings raised by tzinfo gem
+ Time.zone_default = TimeZone['Eastern Time (US & Canada)']
+ with_env_tz 'US/Central' do
+ Time.stubs(:now).returns Time.local(1999, 12, 31, 23)
+ assert_equal Date.new(1999, 12, 31), Date.today
+ assert_equal Date.new(2000, 1, 1), Date.current
+ end
+ end
+ ensure
+ Time.zone_default = nil
+ end
+ end
protected
def with_env_tz(new_tz = 'US/Eastern')
diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb
index df70e82c1d..64fcb4af09 100644
--- a/activesupport/test/core_ext/time_with_zone_test.rb
+++ b/activesupport/test/core_ext/time_with_zone_test.rb
@@ -328,10 +328,18 @@ class TimeWithZoneTest < Test::Unit::TestCase
assert_equal Time.utc(1999, 12, 31, 19), mtime.time
end
end
-
+
def test_method_missing_with_non_time_return_value
silence_warnings do # silence warnings raised by tzinfo gem
+ @twz.time.expects(:foo).returns('bar')
+ assert_equal 'bar', @twz.foo
+ end
+ end
+
+ def test_date_part_value_methods
+ silence_warnings do # silence warnings raised by tzinfo gem
twz = ActiveSupport::TimeWithZone.new(Time.utc(1999,12,31,19,18,17,500), @time_zone)
+ twz.stubs(:method_missing).returns(nil) #ensure these methods are defined directly on class
assert_equal 1999, twz.year
assert_equal 12, twz.month
assert_equal 31, twz.day
diff --git a/activesupport/test/deprecation_test.rb b/activesupport/test/deprecation_test.rb
index ebfa405947..11357e250f 100644
--- a/activesupport/test/deprecation_test.rb
+++ b/activesupport/test/deprecation_test.rb
@@ -149,3 +149,13 @@ class DeprecationTest < Test::Unit::TestCase
assert_nil @last_message
end
end
+
+class DeprecatedIvarTest < Test::Unit::TestCase
+
+ def test_deprecated_ivar
+ @action = ActiveSupport::Deprecation::DeprecatedInstanceVariable.new("fubar", :foobar)
+
+ assert_deprecated(/Instance variable @foobar is deprecated! Call instance method foobar instead/) { assert_equal "fubar", @action }
+ end
+
+end
diff --git a/activesupport/test/time_zone_test.rb b/activesupport/test/time_zone_test.rb
index 2f06c347a1..3757ddcedb 100644
--- a/activesupport/test/time_zone_test.rb
+++ b/activesupport/test/time_zone_test.rb
@@ -173,6 +173,14 @@ class TimeZoneTest < Test::Unit::TestCase
assert_equal zone, twz.time_zone
end
+ def test_parse_string_with_timezone
+ (-11..13).each do |timezone_offset|
+ zone = TimeZone[timezone_offset]
+ twz = zone.parse('1999-12-31 19:00:00')
+ assert_equal twz, zone.parse(twz.to_s)
+ end
+ end
+
def test_parse_with_old_date
silence_warnings do # silence warnings raised by tzinfo gem
zone = TimeZone['Eastern Time (US & Canada)']
@@ -181,6 +189,23 @@ class TimeZoneTest < Test::Unit::TestCase
assert_equal zone, twz.time_zone
end
end
+
+ def test_parse_far_future_date_with_time_zone_offset_in_string
+ silence_warnings do # silence warnings raised by tzinfo gem
+ zone = TimeZone['Eastern Time (US & Canada)']
+ twz = zone.parse('2050-12-31 19:00:00 -10:00') # i.e., 2050-01-01 05:00:00 UTC
+ assert_equal [0,0,0,1,1,2051], twz.to_a[0,6]
+ assert_equal zone, twz.time_zone
+ end
+ end
+
+ def test_parse_returns_nil_when_string_without_date_information_is_passed_in
+ silence_warnings do # silence warnings raised by tzinfo gem
+ zone = TimeZone['Eastern Time (US & Canada)']
+ assert_nil zone.parse('foobar')
+ assert_nil zone.parse(' ')
+ end
+ end
uses_mocha 'TestParseWithIncompleteDate' do
def test_parse_with_incomplete_date
diff --git a/railties/CHANGELOG b/railties/CHANGELOG
index f52206d4d3..2ca1965d97 100644
--- a/railties/CHANGELOG
+++ b/railties/CHANGELOG
@@ -1,5 +1,7 @@
*SVN*
+* script/dbconsole fires up the command-line database client. #102 [Steve Purcell]
+
* Fix bug where plugin init.rb files from frozen gem specs weren't being run. (pjb3) [#122 state:resolved]
* Made the location of the routes file configurable with config.routes_configuration_file (Scott Fleckenstein) [#88]
diff --git a/railties/bin/dbconsole b/railties/bin/dbconsole
new file mode 100755
index 0000000000..caa60ce829
--- /dev/null
+++ b/railties/bin/dbconsole
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../config/boot'
+require 'commands/dbconsole'
diff --git a/railties/helpers/application.rb b/railties/helpers/application.rb
index 9a79f69a41..0a3ed822a4 100644
--- a/railties/helpers/application.rb
+++ b/railties/helpers/application.rb
@@ -7,4 +7,9 @@ class ApplicationController < ActionController::Base
# See ActionController::RequestForgeryProtection for details
# Uncomment the :secret if you're not using the cookie session store
protect_from_forgery # :secret => '<%= app_secret %>'
+
+ # See ActionController::Base for details
+ # Uncomment this to filter the contents of submitted sensitive data parameters
+ # from your application log (in this case, all fields with names like "password").
+ # filter_parameter_logging :password
end
diff --git a/railties/lib/commands/dbconsole.rb b/railties/lib/commands/dbconsole.rb
new file mode 100644
index 0000000000..28c3a3e41f
--- /dev/null
+++ b/railties/lib/commands/dbconsole.rb
@@ -0,0 +1,55 @@
+require 'yaml'
+require 'optparse'
+
+OptionParser.new do |opt|
+ opt.banner = "Usage: dbconsole [environment]"
+ opt.parse!(ARGV)
+ abort opt.to_s unless (0..1).include?(ARGV.size)
+end
+
+env = ARGV.first || ENV['RAILS_ENV'] || 'development'
+unless config = YAML.load_file(RAILS_ROOT + "/config/database.yml")[env]
+ abort "No database is configured for the environment '#{env}'"
+end
+
+
+def find_cmd(*commands)
+ dirs_on_path = ENV['PATH'].to_s.split(File::PATH_SEPARATOR)
+ commands += commands.map{|cmd| "#{cmd}.exe"} if RUBY_PLATFORM =~ /win32/
+ commands.detect do |cmd|
+ dirs_on_path.detect do |path|
+ File.executable? File.join(path, cmd)
+ end
+ end || abort("Couldn't find database client: #{commands.join(', ')}. Check your $PATH and try again.")
+end
+
+case config["adapter"]
+when "mysql"
+ args = {
+ 'host' => '--host',
+ 'port' => '--port',
+ 'socket' => '--socket',
+ 'username' => '--user',
+ 'password' => '--password',
+ 'encoding' => '--default-character-set'
+ }.map { |opt, arg| "#{arg}=#{config[opt]}" if config[opt] }.compact
+
+ args << config['database']
+
+ exec(find_cmd('mysql5', 'mysql'), *args)
+
+when "postgresql"
+ ENV['PGHOST'] = config["host"] if config["host"]
+ ENV['PGPORT'] = config["port"].to_s if config["port"]
+ ENV['PGPASSWORD'] = config["password"].to_s if config["password"]
+ exec(find_cmd('psql'), '-U', config["username"], config["database"])
+
+when "sqlite"
+ exec(find_cmd('sqlite'), config["database"])
+
+when "sqlite3"
+ exec(find_cmd('sqlite3'), config["database"])
+
+else
+ abort "Unknown command-line client for #{config['database']}. Submit a Rails patch to add support!"
+end
diff --git a/railties/lib/initializer.rb b/railties/lib/initializer.rb
index 6db96f0158..dfd43042be 100644
--- a/railties/lib/initializer.rb
+++ b/railties/lib/initializer.rb
@@ -572,11 +572,13 @@ module Rails
attr_accessor :plugin_loader
# Enables or disables plugin reloading. You can get around this setting per plugin.
- # If <tt>reload_plugins?</tt> is false, add this to your plugin's init.rb to make it reloadable:
+ # If <tt>reload_plugins?</tt> is false, add this to your plugin's <tt>init.rb</tt>
+ # to make it reloadable:
#
# Dependencies.load_once_paths.delete lib_path
#
- # If <tt>reload_plugins?</tt> is true, add this to your plugin's init.rb to only load it once:
+ # If <tt>reload_plugins?</tt> is true, add this to your plugin's <tt>init.rb</tt>
+ # to only load it once:
#
# Dependencies.load_once_paths << lib_path
#
@@ -676,7 +678,7 @@ module Rails
YAML::load(ERB.new(IO.read(database_configuration_file)).result)
end
- # The path to the current environment's file (development.rb, etc.). By
+ # The path to the current environment's file (<tt>development.rb</tt>, etc.). By
# default the file is at <tt>config/environments/#{environment}.rb</tt>.
def environment_path
"#{root_path}/config/environments/#{environment}.rb"
diff --git a/railties/lib/rails/plugin.rb b/railties/lib/rails/plugin.rb
index b45ec7de0e..04f7e37a20 100644
--- a/railties/lib/rails/plugin.rb
+++ b/railties/lib/rails/plugin.rb
@@ -1,14 +1,14 @@
module Rails
# The Plugin class should be an object which provides the following methods:
#
- # * +name+ - used during initialisation to order the plugin (based on name and
- # the contents of <tt>config.plugins</tt>)
- # * +valid?+ - returns true if this plugin can be loaded
- # * +load_paths+ - each path within the returned array will be added to the $LOAD_PATH
- # * +load+ - finally 'load' the plugin.
+ # * +name+ - Used during initialisation to order the plugin (based on name and
+ # the contents of <tt>config.plugins</tt>).
+ # * +valid?+ - Returns true if this plugin can be loaded.
+ # * +load_paths+ - Each path within the returned array will be added to the <tt>$LOAD_PATH</tt>.
+ # * +load+ - Finally 'load' the plugin.
#
# These methods are expected by the Rails::Plugin::Locator and Rails::Plugin::Loader classes.
- # The default implementation returns the <tt>lib</tt> directory as its </tt>load_paths</tt>,
+ # The default implementation returns the <tt>lib</tt> directory as its <tt>load_paths</tt>,
# and evaluates <tt>init.rb</tt> when <tt>load</tt> is called.
#
# You can also inspect the about.yml data programmatically:
@@ -31,13 +31,13 @@ module Rails
File.directory?(directory) && (has_lib_directory? || has_init_file?)
end
- # Returns a list of paths this plugin wishes to make available in $LOAD_PATH
+ # Returns a list of paths this plugin wishes to make available in <tt>$LOAD_PATH</tt>.
def load_paths
report_nonexistant_or_empty_plugin! unless valid?
has_lib_directory? ? [lib_path] : []
end
- # Evaluates a plugin's init.rb file
+ # Evaluates a plugin's init.rb file.
def load(initializer)
return if loaded?
report_nonexistant_or_empty_plugin! unless valid?
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 85c417f7cd..2f2dd82682 100644
--- a/railties/lib/rails_generator/generators/applications/app/app_generator.rb
+++ b/railties/lib/rails_generator/generators/applications/app/app_generator.rb
@@ -72,7 +72,7 @@ class AppGenerator < Rails::Generator::Base
m.file "environments/test.rb", "config/environments/test.rb"
# Scripts
- %w( about console destroy generate performance/benchmarker performance/profiler performance/request process/reaper process/spawner process/inspector runner server plugin ).each do |file|
+ %w( about console dbconsole destroy generate performance/benchmarker performance/profiler performance/request process/reaper process/spawner process/inspector runner server plugin ).each do |file|
m.file "bin/#{file}", "script/#{file}", script_options
end
diff --git a/railties/lib/tasks/databases.rake b/railties/lib/tasks/databases.rake
index 20fdcce205..63f71448f8 100644
--- a/railties/lib/tasks/databases.rake
+++ b/railties/lib/tasks/databases.rake
@@ -46,7 +46,7 @@ namespace :db do
@encoding = config[:encoding] || ENV['CHARSET'] || 'utf8'
begin
ActiveRecord::Base.establish_connection(config.merge('database' => 'template1'))
- ActiveRecord::Base.connection.create_database(config['database'], :encoding => @encoding)
+ ActiveRecord::Base.connection.create_database(config['database'], config.merge('encoding' => @encoding))
ActiveRecord::Base.establish_connection(config)
rescue
$stderr.puts $!, *($!.backtrace)
@@ -314,14 +314,9 @@ namespace :db do
ActiveRecord::Base.establish_connection(:test)
ActiveRecord::Base.connection.recreate_database(abcs["test"]["database"])
when "postgresql"
- ENV['PGHOST'] = abcs["test"]["host"] if abcs["test"]["host"]
- ENV['PGPORT'] = abcs["test"]["port"].to_s if abcs["test"]["port"]
- ENV['PGPASSWORD'] = abcs["test"]["password"].to_s if abcs["test"]["password"]
- enc_option = "-E #{abcs["test"]["encoding"]}" if abcs["test"]["encoding"]
-
ActiveRecord::Base.clear_active_connections!
- `dropdb -U "#{abcs["test"]["username"]}" #{abcs["test"]["database"]}`
- `createdb #{enc_option} -U "#{abcs["test"]["username"]}" #{abcs["test"]["database"]}`
+ drop_database(abcs['test'])
+ create_database(abcs['test'])
when "sqlite","sqlite3"
dbfile = abcs["test"]["database"] || abcs["test"]["dbfile"]
File.delete(dbfile) if File.exist?(dbfile)