aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Gemfile2
-rw-r--r--actionmailer/lib/action_mailer.rb3
-rw-r--r--actionmailer/test/abstract_unit.rb3
-rw-r--r--actionpack/CHANGELOG.md13
-rw-r--r--actionpack/Rakefile1
-rw-r--r--actionpack/lib/abstract_controller.rb4
-rw-r--r--actionpack/lib/abstract_controller/callbacks.rb22
-rw-r--r--actionpack/lib/abstract_controller/helpers.rb6
-rw-r--r--actionpack/lib/action_controller/log_subscriber.rb2
-rw-r--r--actionpack/lib/action_controller/metal/exceptions.rb3
-rw-r--r--actionpack/lib/action_controller/metal/head.rb3
-rw-r--r--actionpack/lib/action_controller/metal/http_authentication.rb86
-rw-r--r--actionpack/lib/action_controller/metal/mime_responds.rb3
-rw-r--r--actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb1
-rw-r--r--actionpack/lib/action_dispatch.rb6
-rw-r--r--actionpack/lib/action_dispatch/middleware/cookies.rb11
-rw-r--r--actionpack/lib/action_dispatch/middleware/exception_wrapper.rb4
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb19
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb16
-rw-r--r--actionpack/lib/action_dispatch/testing/integration.rb31
-rw-r--r--actionpack/lib/action_view.rb9
-rw-r--r--actionpack/lib/action_view/base.rb1
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helper.rb66
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb2
-rw-r--r--actionpack/lib/action_view/helpers/date_helper.rb6
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb1
-rw-r--r--actionpack/lib/action_view/helpers/form_tag_helper.rb5
-rw-r--r--actionpack/lib/action_view/helpers/number_helper.rb253
-rw-r--r--actionpack/lib/action_view/helpers/tags/collection_helpers.rb2
-rw-r--r--actionpack/lib/action_view/helpers/text_helper.rb6
-rw-r--r--actionpack/lib/action_view/helpers/translation_helper.rb3
-rw-r--r--actionpack/lib/action_view/template/resolver.rb1
-rw-r--r--actionpack/test/abstract_unit.rb6
-rw-r--r--actionpack/test/controller/integration_test.rb89
-rw-r--r--actionpack/test/controller/mime_responds_test.rb32
-rw-r--r--actionpack/test/controller/new_base/render_text_test.rb4
-rw-r--r--actionpack/test/controller/render_test.rb29
-rw-r--r--actionpack/test/dispatch/mount_test.rb5
-rw-r--r--actionpack/test/dispatch/routing_assertions_test.rb6
-rw-r--r--actionpack/test/dispatch/routing_test.rb110
-rw-r--r--actionpack/test/dispatch/url_generation_test.rb15
-rw-r--r--actionpack/test/template/form_collections_helper_test.rb9
-rw-r--r--actionpack/test/ts_isolated.rb3
-rw-r--r--activemodel/README.rdoc10
-rw-r--r--activemodel/lib/active_model.rb7
-rw-r--r--activemodel/lib/active_model/attribute_methods.rb12
-rw-r--r--activemodel/lib/active_model/serialization.rb4
-rw-r--r--activemodel/lib/active_model/serializers/xml.rb4
-rw-r--r--activemodel/test/cases/helper.rb3
-rw-r--r--activemodel/test/cases/serialization_test.rb18
-rw-r--r--activemodel/test/cases/serializers/xml_serialization_test.rb17
-rw-r--r--activerecord/CHANGELOG.md6
-rw-r--r--activerecord/lib/active_record.rb5
-rw-r--r--activerecord/lib/active_record/aggregations.rb2
-rw-r--r--activerecord/lib/active_record/associations/collection_association.rb8
-rw-r--r--activerecord/lib/active_record/associations/collection_proxy.rb79
-rw-r--r--activerecord/lib/active_record/autosave_association.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/quoting.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb24
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb22
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/column.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb8
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql_adapter.rb8
-rw-r--r--activerecord/lib/active_record/core.rb33
-rw-r--r--activerecord/lib/active_record/fixtures.rb159
-rw-r--r--activerecord/lib/active_record/nested_attributes.rb8
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb11
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder.rb6
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb42
-rw-r--r--activerecord/lib/active_record/relation/spawn_methods.rb17
-rw-r--r--activerecord/lib/active_record/result.rb1
-rw-r--r--activerecord/lib/active_record/transactions.rb8
-rw-r--r--activerecord/test/cases/adapters/mysql/connection_test.rb13
-rw-r--r--activerecord/test/cases/adapters/mysql2/connection_test.rb16
-rw-r--r--activerecord/test/cases/associations/eager_test.rb2
-rw-r--r--activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb7
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb27
-rw-r--r--activerecord/test/cases/associations/join_model_test.rb2
-rw-r--r--activerecord/test/cases/autosave_association_test.rb4
-rw-r--r--activerecord/test/cases/column_test.rb28
-rw-r--r--activerecord/test/cases/connection_adapters/quoting_test.rb13
-rw-r--r--activerecord/test/cases/finder_test.rb5
-rw-r--r--activerecord/test/cases/helper.rb4
-rw-r--r--activerecord/test/cases/relations_test.rb11
-rw-r--r--activerecord/test/cases/transaction_callbacks_test.rb31
-rw-r--r--activerecord/test/schema/schema.rb4
-rw-r--r--activerecord/test/schema/sqlite_specific_schema.rb2
-rw-r--r--activesupport/CHANGELOG.md59
-rw-r--r--activesupport/lib/active_support.rb15
-rw-r--r--activesupport/lib/active_support/cache/file_store.rb6
-rw-r--r--activesupport/lib/active_support/cache/memory_store.rb1
-rw-r--r--activesupport/lib/active_support/callbacks.rb11
-rw-r--r--activesupport/lib/active_support/core_ext/array/wrap.rb3
-rw-r--r--activesupport/lib/active_support/core_ext/class/attribute_accessors.rb134
-rw-r--r--activesupport/lib/active_support/core_ext/hash.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/hash/deep_dup.rb10
-rw-r--r--activesupport/lib/active_support/core_ext/integer/multiple.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/numeric/time.rb6
-rw-r--r--activesupport/lib/active_support/core_ext/object.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/object/deep_dup.rb22
-rw-r--r--activesupport/lib/active_support/core_ext/object/try.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/string/access.rb71
-rw-r--r--activesupport/lib/active_support/core_ext/string/conversions.rb12
-rw-r--r--activesupport/lib/active_support/core_ext/string/exclude.rb7
-rw-r--r--activesupport/lib/active_support/core_ext/string/filters.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/string/inflections.rb6
-rw-r--r--activesupport/lib/active_support/core_ext/string/output_safety.rb2
-rw-r--r--activesupport/lib/active_support/i18n.rb1
-rw-r--r--activesupport/lib/active_support/railtie.rb7
-rw-r--r--activesupport/lib/active_support/ruby/shim.rb15
-rw-r--r--activesupport/lib/active_support/testing/performance.rb2
-rw-r--r--activesupport/lib/active_support/time.rb4
-rw-r--r--activesupport/lib/active_support/time_with_zone.rb4
-rw-r--r--activesupport/test/abstract_unit.rb3
-rw-r--r--activesupport/test/caching_test.rb7
-rw-r--r--activesupport/test/callback_inheritance_test.rb2
-rw-r--r--activesupport/test/callbacks_test.rb6
-rw-r--r--activesupport/test/core_ext/deep_dup_test.rb53
-rw-r--r--activesupport/test/core_ext/hash_ext_test.rb25
-rw-r--r--activesupport/test/core_ext/object_and_class_ext_test.rb14
-rw-r--r--activesupport/test/core_ext/range_ext_test.rb12
-rw-r--r--activesupport/test/ts_isolated.rb2
-rw-r--r--guides/code/getting_started/public/robots.txt2
-rw-r--r--guides/code/getting_started/test/performance/browsing_test.rb2
-rw-r--r--guides/rails_guides/textile_extensions.rb6
-rw-r--r--guides/source/active_record_querying.textile64
-rw-r--r--guides/source/active_support_core_extensions.textile240
-rw-r--r--guides/source/caching_with_rails.textile36
-rw-r--r--guides/source/configuring.textile14
-rw-r--r--guides/source/debugging_rails_applications.textile2
-rw-r--r--guides/source/engines.textile15
-rw-r--r--guides/source/getting_started.textile2
-rw-r--r--guides/source/index.html.erb2
-rw-r--r--guides/source/security.textile2
-rw-r--r--railties/CHANGELOG.md2
-rw-r--r--railties/Rakefile3
-rw-r--r--railties/lib/rails/commands.rb3
-rw-r--r--railties/lib/rails/commands/dbconsole.rb38
-rw-r--r--railties/lib/rails/configuration.rb2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/application.rb5
-rw-r--r--railties/lib/rails/generators/rails/app/templates/public/humans.txt.tt9
-rw-r--r--railties/lib/rails/generators/rails/app/templates/public/robots.txt2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/test/performance/browsing_test.rb2
-rw-r--r--railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb2
-rw-r--r--railties/test/abstract_unit.rb2
-rw-r--r--railties/test/commands/dbconsole_test.rb90
-rw-r--r--railties/test/generators/app_generator_test.rb6
-rw-r--r--railties/test/generators/plugin_new_generator_test.rb7
-rw-r--r--railties/test/isolation/abstract_unit.rb5
150 files changed, 1868 insertions, 834 deletions
diff --git a/Gemfile b/Gemfile
index 005031d843..10202ca877 100644
--- a/Gemfile
+++ b/Gemfile
@@ -50,7 +50,7 @@ instance_eval File.read local_gemfile if File.exists? local_gemfile
platforms :mri do
group :test do
- gem 'ruby-prof'
+ gem 'ruby-prof', '0.10.8'
end
end
diff --git a/actionmailer/lib/action_mailer.rb b/actionmailer/lib/action_mailer.rb
index 1045dd58ef..e45a1cd5ff 100644
--- a/actionmailer/lib/action_mailer.rb
+++ b/actionmailer/lib/action_mailer.rb
@@ -21,9 +21,6 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++
-actionpack_path = File.expand_path('../../../actionpack/lib', __FILE__)
-$:.unshift(actionpack_path) if File.directory?(actionpack_path) && !$:.include?(actionpack_path)
-
require 'abstract_controller'
require 'action_view'
require 'action_mailer/version'
diff --git a/actionmailer/test/abstract_unit.rb b/actionmailer/test/abstract_unit.rb
index 487102b564..99c44179fd 100644
--- a/actionmailer/test/abstract_unit.rb
+++ b/actionmailer/test/abstract_unit.rb
@@ -8,9 +8,6 @@ silence_warnings do
Encoding.default_external = "UTF-8"
end
-lib = File.expand_path("#{File.dirname(__FILE__)}/../lib")
-$:.unshift(lib) unless $:.include?('lib') || $:.include?(lib)
-
require 'minitest/autorun'
require 'action_mailer'
require 'action_mailer/test_case'
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index 96dee33f7b..24cbe35acc 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,5 +1,14 @@
## Rails 4.0.0 (unreleased) ##
+* Copy literal route constraints to defaults so that url generation know about them.
+ The copied constraints are `:protocol`, `:subdomain`, `:domain`, `:host` and `:port`.
+
+ *Andrew White*
+
+* `respond_to` and `respond_with` now raise ActionController::UnknownFormat instead
+ of directly returning head 406. The exception is rescued and converted to 406
+ in the exception handling middleware. *Steven Soroka*
+
* Allows `assert_redirected_to` to match against a regular expression. *Andy Lindeman*
* Add backtrace to development routing error page. *Richard Schneeman*
@@ -12,7 +21,7 @@
* Make current object and counter (when it applies) variables accessible when
rendering templates with :object / :collection. *Carlos Antonio da Silva*
-* JSONP now uses mimetype application/javascript instead of application/json *omjokine*
+* JSONP now uses mimetype application/javascript instead of application/json. *omjokine*
* Allow to lazy load `default_form_builder` by passing a `String` instead of a constant. *Piotr Sarnacki*
@@ -5771,7 +5780,7 @@
== Rendering a collection of partials
The example of partial use describes a familar pattern where a template needs
- to iterate over a array and render a sub template for each of the elements.
+ to iterate over an array and render a sub template for each of the elements.
This pattern has been implemented as a single method that accepts an array and
renders a partial by the same name of as the elements contained within. So the
three-lined example in "Using partials" can be rewritten with a single line:
diff --git a/actionpack/Rakefile b/actionpack/Rakefile
index 17d95bfd1d..50e3bb0d48 100644
--- a/actionpack/Rakefile
+++ b/actionpack/Rakefile
@@ -23,6 +23,7 @@ end
namespace :test do
Rake::TestTask.new(:isolated) do |t|
+ t.libs << 'test'
t.pattern = 'test/ts_isolated.rb'
end
diff --git a/actionpack/lib/abstract_controller.rb b/actionpack/lib/abstract_controller.rb
index cc5878c88e..b95ea5f0b2 100644
--- a/actionpack/lib/abstract_controller.rb
+++ b/actionpack/lib/abstract_controller.rb
@@ -1,9 +1,5 @@
-activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__)
-$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path)
-
require 'action_pack'
require 'active_support/concern'
-require 'active_support/ruby/shim'
require 'active_support/dependencies/autoload'
require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/module/attr_internal'
diff --git a/actionpack/lib/abstract_controller/callbacks.rb b/actionpack/lib/abstract_controller/callbacks.rb
index c0fa28cae9..5705ab590c 100644
--- a/actionpack/lib/abstract_controller/callbacks.rb
+++ b/actionpack/lib/abstract_controller/callbacks.rb
@@ -14,7 +14,7 @@ module AbstractController
# Override AbstractController::Base's process_action to run the
# process_action callbacks around the normal behavior.
def process_action(*args)
- run_callbacks(:process_action, action_name) do
+ run_callbacks(:process_action) do
super
end
end
@@ -163,11 +163,11 @@ module AbstractController
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
# Append a before, after or around filter. See _insert_callbacks
# for details on the allowed parameters.
- def #{filter}_filter(*names, &blk) # def before_filter(*names, &blk)
- _insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options|
- set_callback(:process_action, :#{filter}, name, options) # set_callback(:process_action, :before, name, options)
- end # end
- end # end
+ def #{filter}_filter(*names, &blk) # def before_filter(*names, &blk)
+ _insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options|
+ set_callback(:process_action, :#{filter}, name, options) # set_callback(:process_action, :before, name, options)
+ end # end
+ end # end
# Prepend a before, after or around filter. See _insert_callbacks
# for details on the allowed parameters.
@@ -179,11 +179,11 @@ module AbstractController
# Skip a before, after or around filter. See _insert_callbacks
# for details on the allowed parameters.
- def skip_#{filter}_filter(*names) # def skip_before_filter(*names)
- _insert_callbacks(names) do |name, options| # _insert_callbacks(names) do |name, options|
- skip_callback(:process_action, :#{filter}, name, options) # skip_callback(:process_action, :before, name, options)
- end # end
- end # end
+ def skip_#{filter}_filter(*names) # def skip_before_filter(*names)
+ _insert_callbacks(names) do |name, options| # _insert_callbacks(names) do |name, options|
+ skip_callback(:process_action, :#{filter}, name, options) # skip_callback(:process_action, :before, name, options)
+ end # end
+ end # end
# *_filter is the same as append_*_filter
alias_method :append_#{filter}_filter, :#{filter}_filter # alias_method :append_before_filter, :before_filter
diff --git a/actionpack/lib/abstract_controller/helpers.rb b/actionpack/lib/abstract_controller/helpers.rb
index 77cc4c07d9..4e0672d590 100644
--- a/actionpack/lib/abstract_controller/helpers.rb
+++ b/actionpack/lib/abstract_controller/helpers.rb
@@ -49,9 +49,9 @@ module AbstractController
meths.each do |meth|
_helpers.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1
- def #{meth}(*args, &blk)
- controller.send(%(#{meth}), *args, &blk)
- end
+ def #{meth}(*args, &blk) # def current_user(*args, &blk)
+ controller.send(%(#{meth}), *args, &blk) # controller.send(:current_user, *args, &blk)
+ end # end
ruby_eval
end
end
diff --git a/actionpack/lib/action_controller/log_subscriber.rb b/actionpack/lib/action_controller/log_subscriber.rb
index 4c76f4c43b..11aa393bf9 100644
--- a/actionpack/lib/action_controller/log_subscriber.rb
+++ b/actionpack/lib/action_controller/log_subscriber.rb
@@ -20,7 +20,7 @@ module ActionController
status = payload[:status]
if status.nil? && payload[:exception].present?
- status = Rack::Utils.status_code(ActionDispatch::ExceptionWrapper.new({}, payload[:exception]).status_code)
+ status = ActionDispatch::ExceptionWrapper.new({}, payload[:exception]).status_code
end
message = "Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in %.0fms" % event.duration
message << " (#{additions.join(" | ")})" unless additions.blank?
diff --git a/actionpack/lib/action_controller/metal/exceptions.rb b/actionpack/lib/action_controller/metal/exceptions.rb
index 9a9db0fe5f..90648c37ad 100644
--- a/actionpack/lib/action_controller/metal/exceptions.rb
+++ b/actionpack/lib/action_controller/metal/exceptions.rb
@@ -38,4 +38,7 @@ module ActionController
class UnknownHttpMethod < ActionControllerError #:nodoc:
end
+
+ class UnknownFormat < ActionControllerError #:nodoc:
+ end
end
diff --git a/actionpack/lib/action_controller/metal/head.rb b/actionpack/lib/action_controller/metal/head.rb
index 5bdbde9ebb..2fcd933d32 100644
--- a/actionpack/lib/action_controller/metal/head.rb
+++ b/actionpack/lib/action_controller/metal/head.rb
@@ -20,6 +20,7 @@ module ActionController
options, status = status, nil if status.is_a?(Hash)
status ||= options.delete(:status) || :ok
location = options.delete(:location)
+ content_type = options.delete(:content_type)
options.each do |key, value|
headers[key.to_s.dasherize.split('-').each { |v| v[0] = v[0].chr.upcase }.join('-')] = value.to_s
@@ -29,7 +30,7 @@ module ActionController
self.location = url_for(location) if location
if include_content_headers?(self.status)
- self.content_type = Mime[formats.first] if formats
+ self.content_type = content_type || (Mime[formats.first] if formats)
else
headers.delete('Content-Type')
headers.delete('Content-Length')
diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb
index 87225d74c1..57bb0e2a32 100644
--- a/actionpack/lib/action_controller/metal/http_authentication.rb
+++ b/actionpack/lib/action_controller/metal/http_authentication.rb
@@ -2,8 +2,9 @@ require 'base64'
require 'active_support/core_ext/object/blank'
module ActionController
+ # Makes it dead easy to do HTTP Basic, Digest and Token authentication.
module HttpAuthentication
- # Makes it dead easy to do HTTP \Basic and \Digest authentication.
+ # Makes it dead easy to do HTTP \Basic authentication.
#
# === Simple \Basic example
#
@@ -60,47 +61,6 @@ module ActionController
#
# assert_equal 200, status
# end
- #
- # === Simple \Digest example
- #
- # require 'digest/md5'
- # class PostsController < ApplicationController
- # REALM = "SuperSecret"
- # USERS = {"dhh" => "secret", #plain text password
- # "dap" => Digest::MD5.hexdigest(["dap",REALM,"secret"].join(":"))} #ha1 digest password
- #
- # before_filter :authenticate, :except => [:index]
- #
- # def index
- # render :text => "Everyone can see me!"
- # end
- #
- # def edit
- # render :text => "I'm only accessible if you know the password"
- # end
- #
- # private
- # def authenticate
- # authenticate_or_request_with_http_digest(REALM) do |username|
- # USERS[username]
- # end
- # end
- # end
- #
- # === Notes
- #
- # The +authenticate_or_request_with_http_digest+ block must return the user's password
- # or the ha1 digest hash so the framework can appropriately hash to check the user's
- # credentials. Returning +nil+ will cause authentication to fail.
- #
- # Storing the ha1 hash: MD5(username:realm:password), is better than storing a plain password. If
- # the password file or database is compromised, the attacker would be able to use the ha1 hash to
- # authenticate as the user at this +realm+, but would not have the user's password to try using at
- # other sites.
- #
- # In rare instances, web servers or front proxies strip authorization headers before
- # they reach your application. You can debug this situation by logging all environment
- # variables, and check for HTTP_AUTHORIZATION, amongst others.
module Basic
extend self
@@ -155,6 +115,48 @@ module ActionController
end
end
+ # Makes it dead easy to do HTTP \Digest authentication.
+ #
+ # === Simple \Digest example
+ #
+ # require 'digest/md5'
+ # class PostsController < ApplicationController
+ # REALM = "SuperSecret"
+ # USERS = {"dhh" => "secret", #plain text password
+ # "dap" => Digest::MD5.hexdigest(["dap",REALM,"secret"].join(":"))} #ha1 digest password
+ #
+ # before_filter :authenticate, :except => [:index]
+ #
+ # def index
+ # render :text => "Everyone can see me!"
+ # end
+ #
+ # def edit
+ # render :text => "I'm only accessible if you know the password"
+ # end
+ #
+ # private
+ # def authenticate
+ # authenticate_or_request_with_http_digest(REALM) do |username|
+ # USERS[username]
+ # end
+ # end
+ # end
+ #
+ # === Notes
+ #
+ # The +authenticate_or_request_with_http_digest+ block must return the user's password
+ # or the ha1 digest hash so the framework can appropriately hash to check the user's
+ # credentials. Returning +nil+ will cause authentication to fail.
+ #
+ # Storing the ha1 hash: MD5(username:realm:password), is better than storing a plain password. If
+ # the password file or database is compromised, the attacker would be able to use the ha1 hash to
+ # authenticate as the user at this +realm+, but would not have the user's password to try using at
+ # other sites.
+ #
+ # In rare instances, web servers or front proxies strip authorization headers before
+ # they reach your application. You can debug this situation by logging all environment
+ # variables, and check for HTTP_AUTHORIZATION, amongst others.
module Digest
extend self
diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb
index f467b74256..7917926978 100644
--- a/actionpack/lib/action_controller/metal/mime_responds.rb
+++ b/actionpack/lib/action_controller/metal/mime_responds.rb
@@ -375,8 +375,7 @@ module ActionController #:nodoc:
lookup_context.rendered_format = lookup_context.formats.first
collector
else
- head :not_acceptable
- nil
+ raise ActionController::UnknownFormat
end
end
diff --git a/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb b/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb
index 114b0e73c9..6b269e7a31 100644
--- a/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb
+++ b/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb
@@ -1,6 +1,7 @@
require 'set'
require 'cgi'
require 'active_support/core_ext/class/attribute'
+require 'active_support/core_ext/class/attribute_accessors'
module HTML
class Sanitizer
diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb
index e3b04ac097..1e4ac70f3d 100644
--- a/actionpack/lib/action_dispatch.rb
+++ b/actionpack/lib/action_dispatch.rb
@@ -21,12 +21,6 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++
-activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__)
-$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path)
-
-activemodel_path = File.expand_path('../../../activemodel/lib', __FILE__)
-$:.unshift(activemodel_path) if File.directory?(activemodel_path) && !$:.include?(activemodel_path)
-
require 'active_support'
require 'active_support/dependencies/autoload'
require 'active_support/core_ext/module/attribute_accessors'
diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb
index de9c55d96c..771f075275 100644
--- a/actionpack/lib/action_dispatch/middleware/cookies.rb
+++ b/actionpack/lib/action_dispatch/middleware/cookies.rb
@@ -26,9 +26,9 @@ module ActionDispatch
# # Sets a cookie that expires in 1 hour.
# cookies[:login] = { :value => "XJ-122", :expires => 1.hour.from_now }
#
- # # Sets a signed cookie, which prevents a user from tampering with its value.
+ # # Sets a signed cookie, which prevents users from tampering with its value.
# # The cookie is signed by your app's <tt>config.secret_token</tt> value.
- # # Rails generates this value by default when you create a new Rails app.
+ # # It can be read using the signed method <tt>cookies.signed[:key]</tt>
# cookies.signed[:user_id] = current_user.id
#
# # Sets a "permanent" cookie (which expires in 20 years from now).
@@ -39,9 +39,10 @@ module ActionDispatch
#
# Examples for reading:
#
- # cookies[:user_name] # => "david"
- # cookies.size # => 2
- # cookies[:lat_lon] # => [47.68, -122.37]
+ # cookies[:user_name] # => "david"
+ # cookies.size # => 2
+ # cookies[:lat_lon] # => [47.68, -122.37]
+ # cookies.signed[:login] # => "XJ-122"
#
# Example for deleting:
#
diff --git a/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb
index c0532c80c4..a8f49bd3bd 100644
--- a/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb
+++ b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb
@@ -1,5 +1,6 @@
require 'action_controller/metal/exceptions'
require 'active_support/core_ext/exception'
+require 'active_support/core_ext/class/attribute_accessors'
module ActionDispatch
class ExceptionWrapper
@@ -10,6 +11,7 @@ module ActionDispatch
'AbstractController::ActionNotFound' => :not_found,
'ActionController::MethodNotAllowed' => :method_not_allowed,
'ActionController::NotImplemented' => :not_implemented,
+ 'ActionController::UnknownFormat' => :not_acceptable,
'ActionController::InvalidAuthenticityToken' => :unprocessable_entity
)
@@ -75,4 +77,4 @@ module ActionDispatch
@backtrace_cleaner ||= @env['action_dispatch.backtrace_cleaner']
end
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 2a7d540517..7a22b65c44 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -1,5 +1,6 @@
require 'active_support/core_ext/hash/except'
require 'active_support/core_ext/hash/reverse_merge'
+require 'active_support/core_ext/hash/slice'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/enumerable'
require 'active_support/inflector'
@@ -100,6 +101,10 @@ module ActionDispatch
raise ArgumentError, "Regexp multiline option not allowed in routing requirements: #{requirement.inspect}"
end
end
+
+ if @options[:constraints].is_a?(Hash)
+ (@options[:defaults] ||= {}).reverse_merge!(defaults_from_constraints(@options[:constraints]))
+ end
end
# match "account/overview"
@@ -245,6 +250,11 @@ module ActionDispatch
def default_action
@options[:action] || @scope[:action]
end
+
+ def defaults_from_constraints(constraints)
+ url_keys = [:protocol, :subdomain, :domain, :host, :port]
+ constraints.slice(*url_keys).select{ |k, v| v.is_a?(String) || v.is_a?(Fixnum) }
+ end
end
# Invokes Rack::Mount::Utils.normalize path and ensure that
@@ -641,6 +651,10 @@ module ActionDispatch
block, options[:constraints] = options[:constraints], {}
end
+ if options[:constraints].is_a?(Hash)
+ (options[:defaults] ||= {}).reverse_merge!(defaults_from_constraints(options[:constraints]))
+ end
+
scope_options.each do |option|
if value = options.delete(option)
recover[option] = @scope[option]
@@ -849,6 +863,11 @@ module ActionDispatch
def override_keys(child) #:nodoc:
child.key?(:only) || child.key?(:except) ? [:only, :except] : []
end
+
+ def defaults_from_constraints(constraints)
+ url_keys = [:protocol, :subdomain, :domain, :host, :port]
+ constraints.slice(*url_keys).select{ |k, v| v.is_a?(String) || v.is_a?(Fixnum) }
+ end
end
# Resource routing allows you to quickly declare all of the common routes
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index 7abd7bd008..0ae668d42a 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -103,7 +103,7 @@ module ActionDispatch
inner_options = args.extract_options!
result = options.dup
- if args.any?
+ if args.size > 0
keys = segment_keys
if args.size < keys.size - 1 # take format into account
keys -= self.url_options.keys if self.respond_to?(:url_options)
@@ -188,7 +188,8 @@ module ActionDispatch
remove_possible_method :#{selector}
def #{selector}(*args)
if #{optimize_helper?(route)} && args.size == #{route.required_parts.size} && !args.last.is_a?(Hash) && optimize_routes_generation?
- options = #{options.inspect}.merge!(url_options)
+ options = #{options.inspect}
+ options.merge!(url_options) if respond_to?(:url_options)
options[:path] = "#{optimized_helper(route)}"
ActionDispatch::Http::URL.url_for(options)
else
@@ -459,12 +460,12 @@ module ActionDispatch
normalize_options!
normalize_controller_action_id!
use_relative_controller!
- controller.sub!(%r{^/}, '') if controller
+ normalize_controller!
handle_nil_action!
end
def controller
- @controller ||= @options[:controller]
+ @options[:controller]
end
def current_controller
@@ -521,10 +522,15 @@ module ActionDispatch
old_parts = current_controller.split('/')
size = controller.count("/") + 1
parts = old_parts[0...-size] << controller
- @controller = @options[:controller] = parts.join("/")
+ @options[:controller] = parts.join("/")
end
end
+ # Remove leading slashes from controllers
+ def normalize_controller!
+ @options[:controller] = controller.sub(%r{^/}, '') if controller
+ end
+
# This handles the case of :action => nil being explicitly passed.
# It is identical to :action => "index"
def handle_nil_action!
diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb
index 69d54f6981..08fd28d72d 100644
--- a/actionpack/lib/action_dispatch/testing/integration.rb
+++ b/actionpack/lib/action_dispatch/testing/integration.rb
@@ -201,9 +201,16 @@ module ActionDispatch
reset!
end
- remove_method :default_url_options
- def default_url_options
- { :host => host, :protocol => https? ? "https" : "http" }
+ def url_options
+ @url_options ||= default_url_options.dup.tap do |url_options|
+ url_options.reverse_merge!(controller.url_options) if controller
+
+ if @app.respond_to?(:routes) && @app.routes.respond_to?(:default_url_options)
+ url_options.reverse_merge!(@app.routes.default_url_options)
+ end
+
+ url_options.reverse_merge!(:host => host, :protocol => https? ? "https" : "http")
+ end
end
# Resets the instance. This can be used to reset the state information
@@ -216,6 +223,7 @@ module ActionDispatch
@controller = @request = @response = nil
@_mock_session = nil
@request_count = 0
+ @url_options = nil
self.host = DEFAULT_HOST
self.remote_addr = "127.0.0.1"
@@ -310,6 +318,7 @@ module ActionDispatch
response = _mock_session.last_response
@response = ActionDispatch::TestResponse.new(response.status, response.headers, response.body)
@html_document = nil
+ @url_options = nil
@controller = session.last_request.env['action_controller.instance']
@@ -367,12 +376,14 @@ module ActionDispatch
end
end
- extend ActiveSupport::Concern
- include ActionDispatch::Routing::UrlFor
+ def default_url_options
+ reset! unless integration_session
+ integration_session.default_url_options
+ end
- def url_options
+ def default_url_options=(options)
reset! unless integration_session
- integration_session.url_options
+ integration_session.default_url_options = options
end
def respond_to?(method, include_private = false)
@@ -476,6 +487,7 @@ module ActionDispatch
class IntegrationTest < ActiveSupport::TestCase
include Integration::Runner
include ActionController::TemplateAssertions
+ include ActionDispatch::Routing::UrlFor
@@app = nil
@@ -495,5 +507,10 @@ module ActionDispatch
def app
super || self.class.app
end
+
+ def url_options
+ reset! unless integration_session
+ integration_session.url_options
+ end
end
end
diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb
index 349a3fcc6e..3823f87027 100644
--- a/actionpack/lib/action_view.rb
+++ b/actionpack/lib/action_view.rb
@@ -21,9 +21,7 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++
-require 'active_support/ruby/shim'
-require 'active_support/core_ext/class/attribute_accessors'
-
+require 'active_support'
require 'action_pack'
module ActionView
@@ -78,7 +76,8 @@ module ActionView
ENCODING_FLAG = '#.*coding[:=]\s*(\S+)[ \t]*'
end
-require 'active_support/i18n'
require 'active_support/core_ext/string/output_safety'
-I18n.load_path << "#{File.dirname(__FILE__)}/action_view/locale/en.yml"
+ActiveSupport.on_load(:i18n) do
+ I18n.load_path << "#{File.dirname(__FILE__)}/action_view/locale/en.yml"
+end
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb
index 5f81f24a2e..f98648d930 100644
--- a/actionpack/lib/action_view/base.rb
+++ b/actionpack/lib/action_view/base.rb
@@ -1,6 +1,7 @@
require 'active_support/core_ext/module/attr_internal'
require 'active_support/core_ext/module/delegation'
require 'active_support/core_ext/class/attribute'
+require 'active_support/core_ext/class/attribute_accessors'
require 'active_support/ordered_options'
require 'action_view/log_subscriber'
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
index 7985683a72..a8d6213b13 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -13,15 +13,16 @@ module ActionView
# the assets exist before linking to them:
#
# image_tag("rails.png")
- # # => <img alt="Rails" src="/images/rails.png?1230601161" />
+ # # => <img alt="Rails" src="/assets/rails.png" />
# stylesheet_link_tag("application")
- # # => <link href="/stylesheets/application.css?1232285206" media="screen" rel="stylesheet" />
+ # # => <link href="/assets/application.css?body=1" media="screen" rel="stylesheet" />
+ #
#
# === Using asset hosts
#
# By default, Rails links to these assets on the current host in the public
# folder, but you can direct Rails to link to assets from a dedicated asset
- # server by setting ActionController::Base.asset_host in the application
+ # server by setting <tt>ActionController::Base.asset_host</tt> in the application
# configuration, typically in <tt>config/environments/production.rb</tt>.
# For example, you'd define <tt>assets.example.com</tt> to be your asset
# host this way, inside the <tt>configure</tt> block of your environment-specific
@@ -32,9 +33,9 @@ module ActionView
# Helpers take that into account:
#
# image_tag("rails.png")
- # # => <img alt="Rails" src="http://assets.example.com/images/rails.png?1230601161" />
+ # # => <img alt="Rails" src="http://assets.example.com/assets/rails.png" />
# stylesheet_link_tag("application")
- # # => <link href="http://assets.example.com/stylesheets/application.css?1232285206" media="screen" rel="stylesheet" />
+ # # => <link href="http://assets.example.com/assets/application.css" media="screen" rel="stylesheet" />
#
# Browsers typically open at most two simultaneous connections to a single
# host, which means your assets often have to wait for other assets to finish
@@ -45,9 +46,9 @@ module ActionView
# will open eight simultaneous connections rather than two.
#
# image_tag("rails.png")
- # # => <img alt="Rails" src="http://assets0.example.com/images/rails.png?1230601161" />
+ # # => <img alt="Rails" src="http://assets0.example.com/assets/rails.png" />
# stylesheet_link_tag("application")
- # # => <link href="http://assets2.example.com/stylesheets/application.css?1232285206" media="screen" rel="stylesheet" />
+ # # => <link href="http://assets2.example.com/assets/application.css" media="screen" rel="stylesheet" />
#
# To do this, you can either setup four actual hosts, or you can use wildcard
# DNS to CNAME the wildcard to a single asset host. You can read more about
@@ -64,29 +65,28 @@ module ActionView
# "http://assets#{Digest::MD5.hexdigest(source).to_i(16) % 2 + 1}.example.com"
# }
# image_tag("rails.png")
- # # => <img alt="Rails" src="http://assets1.example.com/images/rails.png?1230601161" />
+ # # => <img alt="Rails" src="http://assets1.example.com/assets/rails.png" />
# stylesheet_link_tag("application")
- # # => <link href="http://assets2.example.com/stylesheets/application.css?1232285206" media="screen" rel="stylesheet" />
+ # # => <link href="http://assets2.example.com/assets/application.css" media="screen" rel="stylesheet" />
#
# The example above generates "http://assets1.example.com" and
# "http://assets2.example.com". This option is useful for example if
# you need fewer/more than four hosts, custom host names, etc.
#
# As you see the proc takes a +source+ parameter. That's a string with the
- # absolute path of the asset with any extensions and timestamps in place,
- # for example "/images/rails.png?1230601161".
+ # absolute path of the asset, for example "/assets/rails.png".
#
# ActionController::Base.asset_host = Proc.new { |source|
- # if source.starts_with?('/images')
- # "http://images.example.com"
+ # if source.ends_with?('.css')
+ # "http://stylesheets.example.com"
# else
# "http://assets.example.com"
# end
# }
# image_tag("rails.png")
- # # => <img alt="Rails" src="http://images.example.com/images/rails.png?1230601161" />
+ # # => <img alt="Rails" src="http://assets.example.com/assets/rails.png" />
# stylesheet_link_tag("application")
- # # => <link href="http://assets.example.com/stylesheets/application.css?1232285206" media="screen" rel="stylesheet" />
+ # # => <link href="http://stylesheets.example.com/assets/application.css" media="screen" rel="stylesheet" />
#
# Alternatively you may ask for a second parameter +request+. That one is
# particularly useful for serving assets from an SSL-protected page. The
@@ -261,13 +261,13 @@ module ActionView
}.merge(options.symbolize_keys))
end
- # Computes the path to an image asset in the public images directory.
+ # Computes the path to an image asset.
# Full paths from the document root will be passed through.
# Used internally by +image_tag+ to build the image path:
#
- # image_path("edit") # => "/images/edit"
- # image_path("edit.png") # => "/images/edit.png"
- # image_path("icons/edit.png") # => "/images/icons/edit.png"
+ # image_path("edit") # => "/assets/edit"
+ # image_path("edit.png") # => "/assets/edit.png"
+ # image_path("icons/edit.png") # => "/assets/icons/edit.png"
# image_path("/icons/edit.png") # => "/icons/edit.png"
# image_path("http://www.example.com/img/edit.png") # => "http://www.example.com/img/edit.png"
#
@@ -279,7 +279,7 @@ module ActionView
end
alias_method :path_to_image, :image_path # aliased to avoid conflicts with an image_path named route
- # Computes the full URL to an image asset in the public images directory.
+ # Computes the full URL to an image asset.
# This will use +image_path+ internally, so most of their behaviors will be the same.
def image_url(source)
URI.join(current_host, path_to_image(source)).to_s
@@ -330,13 +330,13 @@ module ActionView
end
alias_method :url_to_audio, :audio_url # aliased to avoid conflicts with an audio_url named route
- # Computes the path to a font asset in the public fonts directory.
+ # Computes the path to a font asset.
# Full paths from the document root will be passed through.
#
# ==== Examples
- # font_path("font") # => /fonts/font
- # font_path("font.ttf") # => /fonts/font.ttf
- # font_path("dir/font.ttf") # => /fonts/dir/font.ttf
+ # font_path("font") # => /assets/font
+ # font_path("font.ttf") # => /assets/font.ttf
+ # font_path("dir/font.ttf") # => /assets/dir/font.ttf
# font_path("/dir/font.ttf") # => /dir/font.ttf
# font_path("http://www.example.com/dir/font.ttf") # => http://www.example.com/dir/font.ttf
def font_path(source)
@@ -344,7 +344,7 @@ module ActionView
end
alias_method :path_to_font, :font_path # aliased to avoid conflicts with an font_path named route
- # Computes the full URL to a font asset in the public fonts directory.
+ # Computes the full URL to a font asset.
# This will use +font_path+ internally, so most of their behaviors will be the same.
def font_url(source)
URI.join(current_host, path_to_font(source)).to_s
@@ -352,7 +352,7 @@ module ActionView
alias_method :url_to_font, :font_url # aliased to avoid conflicts with an font_url named route
# Returns an html image tag for the +source+. The +source+ can be a full
- # path or a file that exists in your public images directory.
+ # path or a file.
#
# ==== Options
# You can add HTML attributes using the +options+. The +options+ supports
@@ -369,21 +369,21 @@ module ActionView
#
# ==== Examples
# image_tag("icon") # =>
- # <img src="/images/icon" alt="Icon" />
+ # <img src="/assets/icon" alt="Icon" />
# image_tag("icon.png") # =>
- # <img src="/images/icon.png" alt="Icon" />
+ # <img src="/assets/icon.png" alt="Icon" />
# image_tag("icon.png", :size => "16x10", :alt => "Edit Entry") # =>
- # <img src="/images/icon.png" width="16" height="10" alt="Edit Entry" />
+ # <img src="/assets/icon.png" width="16" height="10" alt="Edit Entry" />
# image_tag("/icons/icon.gif", :size => "16x16") # =>
# <img src="/icons/icon.gif" width="16" height="16" alt="Icon" />
# image_tag("/icons/icon.gif", :height => '32', :width => '32') # =>
# <img alt="Icon" height="32" src="/icons/icon.gif" width="32" />
# image_tag("/icons/icon.gif", :class => "menu_icon") # =>
# <img alt="Icon" class="menu_icon" src="/icons/icon.gif" />
- # image_tag("mouse.png", :mouseover => "/images/mouse_over.png") # =>
- # <img src="/images/mouse.png" onmouseover="this.src='/images/mouse_over.png'" onmouseout="this.src='/images/mouse.png'" alt="Mouse" />
+ # image_tag("mouse.png", :mouseover => "/assets/mouse_over.png") # =>
+ # <img src="/assets/mouse.png" onmouseover="this.src='/assets/mouse_over.png'" onmouseout="this.src='/assets/mouse.png'" alt="Mouse" />
# image_tag("mouse.png", :mouseover => image_path("mouse_over.png")) # =>
- # <img src="/images/mouse.png" onmouseover="this.src='/images/mouse_over.png'" onmouseout="this.src='/images/mouse.png'" alt="Mouse" />
+ # <img src="/assets/mouse.png" onmouseover="this.src='/assets/mouse_over.png'" onmouseout="this.src='/assets/mouse.png'" alt="Mouse" />
def image_tag(source, options={})
options = options.symbolize_keys
@@ -433,7 +433,7 @@ module ActionView
# video_tag("trailer.ogg", :controls => true, :autobuffer => true) # =>
# <video autobuffer="autobuffer" controls="controls" src="/videos/trailer.ogg" />
# video_tag("trailer.m4v", :size => "16x10", :poster => "screenshot.png") # =>
- # <video src="/videos/trailer.m4v" width="16" height="10" poster="/images/screenshot.png" />
+ # <video src="/videos/trailer.m4v" width="16" height="10" poster="/assets/screenshot.png" />
# video_tag("/trailers/hd.avi", :size => "16x16") # =>
# <video src="/trailers/hd.avi" width="16" height="16" />
# video_tag("/trailers/hd.avi", :height => '32', :width => '32') # =>
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb
index e3329c0345..7bff0c1149 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb
@@ -136,8 +136,6 @@ module ActionView
# # <script src="/javascripts/rails.js?1284139606"></script>
# # <script src="/javascripts/application.js?1284139606"></script>
#
- # * = The application.js file is only referenced if it exists
- #
# You can also include all JavaScripts in the +javascripts+ directory using <tt>:all</tt> as the source:
#
# javascript_include_tag :all
diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb
index 6bd8e62e0d..f0a593d2c1 100644
--- a/actionpack/lib/action_view/helpers/date_helper.rb
+++ b/actionpack/lib/action_view/helpers/date_helper.rb
@@ -66,12 +66,12 @@ module ActionView
# distance_of_time_in_words(Time.now, Time.now) # => less than a minute
#
def distance_of_time_in_words(from_time, to_time = 0, include_seconds_or_options = {}, options = {})
- unless include_seconds_or_options.is_a?(Hash)
+ if include_seconds_or_options.is_a?(Hash)
+ options = include_seconds_or_options
+ else
ActiveSupport::Deprecation.warn "distance_of_time_in_words and time_ago_in_words now accept :include_seconds " +
"as a part of options hash, not a boolean argument", caller
options[:include_seconds] ||= !!include_seconds_or_options
- else
- options = include_seconds_or_options
end
from_time = from_time.to_time if from_time.respond_to?(:to_time)
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index 67f2abe509..0fbd6ac41b 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -5,6 +5,7 @@ require 'action_view/helpers/form_tag_helper'
require 'action_view/helpers/active_model_helper'
require 'action_view/helpers/tags'
require 'active_support/core_ext/class/attribute'
+require 'active_support/core_ext/class/attribute_accessors'
require 'active_support/core_ext/hash/slice'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/string/output_safety'
diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb
index ef35a411ad..248cc2f6a3 100644
--- a/actionpack/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb
@@ -408,7 +408,7 @@ module ActionView
# # => <input class="form_submit" name="commit" type="submit" />
#
# submit_tag "Edit", :disable_with => "Editing...", :class => "edit_button"
- # # => <input class="edit_button" data-disable_with="Editing..." name="commit" type="submit" value="Edit" />
+ # # => <input class="edit_button" data-disable-with="Editing..." name="commit" type="submit" value="Edit" />
#
# submit_tag "Save", :confirm => "Are you sure?"
# # => <input name='commit' type='submit' value='Save' data-confirm="Are you sure?" />
@@ -502,6 +502,9 @@ module ActionView
#
# image_submit_tag("agree.png", :disabled => true, :class => "agree_disagree_button")
# # => <input class="agree_disagree_button" disabled="disabled" src="/images/agree.png" type="image" />
+ #
+ # image_submit_tag("save.png", :confirm => "Are you sure?")
+ # # => <input src="/images/save.png" data-confirm="Are you sure?" type="image" />
def image_submit_tag(source, options = {})
options = options.stringify_keys
diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb
index 2011351bd2..dfc26acfad 100644
--- a/actionpack/lib/action_view/helpers/number_helper.rb
+++ b/actionpack/lib/action_view/helpers/number_helper.rb
@@ -28,17 +28,20 @@ module ActionView
end
end
- # Formats a +number+ into a US phone number (e.g., (555) 123-9876). You can customize the format
- # in the +options+ hash.
+ # Formats a +number+ into a US phone number (e.g., (555)
+ # 123-9876). You can customize the format in the +options+ hash.
#
# ==== Options
#
- # * <tt>:area_code</tt> - Adds parentheses around the area code.
- # * <tt>:delimiter</tt> - Specifies the delimiter to use (defaults to "-").
- # * <tt>:extension</tt> - Specifies an extension to add to the end of the
- # generated number.
- # * <tt>:country_code</tt> - Sets the country code for the phone number.
- # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when the argument is invalid.
+ # * <tt>:area_code</tt> - Adds parentheses around the area code.
+ # * <tt>:delimiter</tt> - Specifies the delimiter to use
+ # (defaults to "-").
+ # * <tt>:extension</tt> - Specifies an extension to add to the
+ # end of the generated number.
+ # * <tt>:country_code</tt> - Sets the country code for the phone
+ # number.
+ # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
+ # the argument is invalid.
#
# ==== Examples
#
@@ -81,24 +84,31 @@ module ActionView
ERB::Util.html_escape(str)
end
- # Formats a +number+ into a currency string (e.g., $13.65). You can customize the format
- # in the +options+ hash.
+ # Formats a +number+ into a currency string (e.g., $13.65). You
+ # can customize the format in the +options+ hash.
#
# ==== Options
#
- # * <tt>:locale</tt> - Sets the locale to be used for formatting (defaults to current locale).
- # * <tt>:precision</tt> - Sets the level of precision (defaults to 2).
- # * <tt>:unit</tt> - Sets the denomination of the currency (defaults to "$").
- # * <tt>:separator</tt> - Sets the separator between the units (defaults to ".").
- # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to ",").
- # * <tt>:format</tt> - Sets the format for non-negative numbers (defaults to "%u%n").
- # Fields are <tt>%u</tt> for the currency, and <tt>%n</tt>
- # for the number.
- # * <tt>:negative_format</tt> - Sets the format for negative numbers (defaults to prepending
- # an hyphen to the formatted number given by <tt>:format</tt>).
- # Accepts the same fields than <tt>:format</tt>, except
- # <tt>%n</tt> is here the absolute value of the number.
- # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when the argument is invalid.
+ # * <tt>:locale</tt> - Sets the locale to be used for formatting
+ # (defaults to current locale).
+ # * <tt>:precision</tt> - Sets the level of precision (defaults
+ # to 2).
+ # * <tt>:unit</tt> - Sets the denomination of the currency
+ # (defaults to "$").
+ # * <tt>:separator</tt> - Sets the separator between the units
+ # (defaults to ".").
+ # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
+ # to ",").
+ # * <tt>:format</tt> - Sets the format for non-negative numbers
+ # (defaults to "%u%n"). Fields are <tt>%u</tt> for the
+ # currency, and <tt>%n</tt> for the number.
+ # * <tt>:negative_format</tt> - Sets the format for negative
+ # numbers (defaults to prepending an hyphen to the formatted
+ # number given by <tt>:format</tt>). Accepts the same fields
+ # than <tt>:format</tt>, except <tt>%n</tt> is here the
+ # absolute value of the number.
+ # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
+ # the argument is invalid.
#
# ==== Examples
#
@@ -148,23 +158,29 @@ module ActionView
end
end
- # Formats a +number+ as a percentage string (e.g., 65%). You can customize the format in the +options+ hash.
+ # Formats a +number+ as a percentage string (e.g., 65%). You can
+ # customize the format in the +options+ hash.
#
# ==== Options
#
- # * <tt>:locale</tt> - Sets the locale to be used for formatting (defaults to current
- # locale).
- # * <tt>:precision</tt> - Sets the precision of the number (defaults to 3).
- # * <tt>:significant</tt> - If +true+, precision will be the # of significant_digits. If +false+,
- # the # of fractional digits (defaults to +false+).
- # * <tt>:separator</tt> - Sets the separator between the fractional and integer digits (defaults
- # to ".").
- # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to "").
- # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes insignificant zeros after the decimal separator
- # (defaults to +false+).
- # * <tt>:format</tt> - Specifies the format of the percentage string
- # The number field is <tt>%n</tt> (defaults to "%n%").
- # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when the argument is invalid.
+ # * <tt>:locale</tt> - Sets the locale to be used for formatting
+ # (defaults to current locale).
+ # * <tt>:precision</tt> - Sets the precision of the number
+ # (defaults to 3).
+ # * <tt>:significant</tt> - If +true+, precision will be the #
+ # of significant_digits. If +false+, the # of fractional
+ # digits (defaults to +false+).
+ # * <tt>:separator</tt> - Sets the separator between the
+ # fractional and integer digits (defaults to ".").
+ # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
+ # to "").
+ # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
+ # insignificant zeros after the decimal separator (defaults to
+ # +false+).
+ # * <tt>:format</tt> - Specifies the format of the percentage
+ # string The number field is <tt>%n</tt> (defaults to "%n%").
+ # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
+ # the argument is invalid.
#
# ==== Examples
#
@@ -200,15 +216,20 @@ module ActionView
end
end
- # Formats a +number+ with grouped thousands using +delimiter+ (e.g., 12,324). You can
- # customize the format in the +options+ hash.
+ # Formats a +number+ with grouped thousands using +delimiter+
+ # (e.g., 12,324). You can customize the format in the +options+
+ # hash.
#
# ==== Options
#
- # * <tt>:locale</tt> - Sets the locale to be used for formatting (defaults to current locale).
- # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to ",").
- # * <tt>:separator</tt> - Sets the separator between the fractional and integer digits (defaults to ".").
- # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when the argument is invalid.
+ # * <tt>:locale</tt> - Sets the locale to be used for formatting
+ # (defaults to current locale).
+ # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
+ # to ",").
+ # * <tt>:separator</tt> - Sets the separator between the
+ # fractional and integer digits (defaults to ".").
+ # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
+ # the argument is invalid.
#
# ==== Examples
#
@@ -236,23 +257,32 @@ module ActionView
safe_join(parts, options[:separator])
end
- # Formats a +number+ with the specified level of <tt>:precision</tt> (e.g., 112.32 has a precision
- # of 2 if +:significant+ is +false+, and 5 if +:significant+ is +true+).
+ # Formats a +number+ with the specified level of
+ # <tt>:precision</tt> (e.g., 112.32 has a precision of 2 if
+ # +:significant+ is +false+, and 5 if +:significant+ is +true+).
# You can customize the format in the +options+ hash.
#
# ==== Options
- # * <tt>:locale</tt> - Sets the locale to be used for formatting (defaults to current locale).
- # * <tt>:precision</tt> - Sets the precision of the number (defaults to 3).
- # * <tt>:significant</tt> - If +true+, precision will be the # of significant_digits. If +false+,
- # the # of fractional digits (defaults to +false+).
- # * <tt>:separator</tt> - Sets the separator between the fractional and integer digits (defaults
- # to ".").
- # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to "").
- # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes insignificant zeros after the decimal separator
- # (defaults to +false+).
- # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when the argument is invalid.
+ #
+ # * <tt>:locale</tt> - Sets the locale to be used for formatting
+ # (defaults to current locale).
+ # * <tt>:precision</tt> - Sets the precision of the number
+ # (defaults to 3).
+ # * <tt>:significant</tt> - If +true+, precision will be the #
+ # of significant_digits. If +false+, the # of fractional
+ # digits (defaults to +false+).
+ # * <tt>:separator</tt> - Sets the separator between the
+ # fractional and integer digits (defaults to ".").
+ # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
+ # to "").
+ # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
+ # insignificant zeros after the decimal separator (defaults to
+ # +false+).
+ # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
+ # the argument is invalid.
#
# ==== Examples
+ #
# number_with_precision(111.2345) # => 111.235
# number_with_precision(111.2345, :precision => 2) # => 111.23
# number_with_precision(13, :precision => 5) # => 13.00000
@@ -305,23 +335,37 @@ module ActionView
STORAGE_UNITS = [:byte, :kb, :mb, :gb, :tb].freeze
- # Formats the bytes in +number+ into a more understandable representation
- # (e.g., giving it 1500 yields 1.5 KB). This method is useful for
- # reporting file sizes to users. You can customize the
- # format in the +options+ hash.
+ # Formats the bytes in +number+ into a more understandable
+ # representation (e.g., giving it 1500 yields 1.5 KB). This
+ # method is useful for reporting file sizes to users. You can
+ # customize the format in the +options+ hash.
#
- # See <tt>number_to_human</tt> if you want to pretty-print a generic number.
+ # See <tt>number_to_human</tt> if you want to pretty-print a
+ # generic number.
#
# ==== Options
- # * <tt>:locale</tt> - Sets the locale to be used for formatting (defaults to current locale).
- # * <tt>:precision</tt> - Sets the precision of the number (defaults to 3).
- # * <tt>:significant</tt> - If +true+, precision will be the # of significant_digits. If +false+, the # of fractional digits (defaults to +true+)
- # * <tt>:separator</tt> - Sets the separator between the fractional and integer digits (defaults to ".").
- # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to "").
- # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes insignificant zeros after the decimal separator (defaults to +true+)
- # * <tt>:prefix</tt> - If +:si+ formats the number using the SI prefix (defaults to :binary)
- # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when the argument is invalid.
+ #
+ # * <tt>:locale</tt> - Sets the locale to be used for formatting
+ # (defaults to current locale).
+ # * <tt>:precision</tt> - Sets the precision of the number
+ # (defaults to 3).
+ # * <tt>:significant</tt> - If +true+, precision will be the #
+ # of significant_digits. If +false+, the # of fractional
+ # digits (defaults to +true+)
+ # * <tt>:separator</tt> - Sets the separator between the
+ # fractional and integer digits (defaults to ".").
+ # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
+ # to "").
+ # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
+ # insignificant zeros after the decimal separator (defaults to
+ # +true+)
+ # * <tt>:prefix</tt> - If +:si+ formats the number using the SI
+ # prefix (defaults to :binary)
+ # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
+ # the argument is invalid.
+ #
# ==== Examples
+ #
# number_to_human_size(123) # => 123 Bytes
# number_to_human_size(1234) # => 1.21 KB
# number_to_human_size(12345) # => 12.1 KB
@@ -332,8 +376,10 @@ module ActionView
# number_to_human_size(483989, :precision => 2) # => 470 KB
# number_to_human_size(1234567, :precision => 2, :separator => ',') # => 1,2 MB
#
- # Non-significant zeros after the fractional separator are stripped out by default (set
- # <tt>:strip_insignificant_zeros</tt> to +false+ to change that):
+ # Non-significant zeros after the fractional separator are
+ # stripped out by default (set
+ # <tt>:strip_insignificant_zeros</tt> to +false+ to change
+ # that):
# number_to_human_size(1234567890123, :precision => 5) # => "1.1229 TB"
# number_to_human_size(524288000, :precision => 5) # => "500 MB"
def number_to_human_size(number, options = {})
@@ -371,33 +417,55 @@ module ActionView
DECIMAL_UNITS = {0 => :unit, 1 => :ten, 2 => :hundred, 3 => :thousand, 6 => :million, 9 => :billion, 12 => :trillion, 15 => :quadrillion,
-1 => :deci, -2 => :centi, -3 => :mili, -6 => :micro, -9 => :nano, -12 => :pico, -15 => :femto}.freeze
- # Pretty prints (formats and approximates) a number in a way it is more readable by humans
- # (eg.: 1200000000 becomes "1.2 Billion"). This is useful for numbers that
- # can get very large (and too hard to read).
+ # Pretty prints (formats and approximates) a number in a way it
+ # is more readable by humans (eg.: 1200000000 becomes "1.2
+ # Billion"). This is useful for numbers that can get very large
+ # (and too hard to read).
#
- # See <tt>number_to_human_size</tt> if you want to print a file size.
+ # See <tt>number_to_human_size</tt> if you want to print a file
+ # size.
#
- # You can also define you own unit-quantifier names if you want to use other decimal units
- # (eg.: 1500 becomes "1.5 kilometers", 0.150 becomes "150 milliliters", etc). You may define
- # a wide range of unit quantifiers, even fractional ones (centi, deci, mili, etc).
+ # You can also define you own unit-quantifier names if you want
+ # to use other decimal units (eg.: 1500 becomes "1.5
+ # kilometers", 0.150 becomes "150 milliliters", etc). You may
+ # define a wide range of unit quantifiers, even fractional ones
+ # (centi, deci, mili, etc).
#
# ==== Options
- # * <tt>:locale</tt> - Sets the locale to be used for formatting (defaults to current locale).
- # * <tt>:precision</tt> - Sets the precision of the number (defaults to 3).
- # * <tt>:significant</tt> - If +true+, precision will be the # of significant_digits. If +false+, the # of fractional digits (defaults to +true+)
- # * <tt>:separator</tt> - Sets the separator between the fractional and integer digits (defaults to ".").
- # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to "").
- # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes insignificant zeros after the decimal separator (defaults to +true+)
- # * <tt>:units</tt> - A Hash of unit quantifier names. Or a string containing an i18n scope where to find this hash. It might have the following keys:
- # * *integers*: <tt>:unit</tt>, <tt>:ten</tt>, <tt>:hundred</tt>, <tt>:thousand</tt>, <tt>:million</tt>, <tt>:billion</tt>, <tt>:trillion</tt>, <tt>:quadrillion</tt>
- # * *fractionals*: <tt>:deci</tt>, <tt>:centi</tt>, <tt>:mili</tt>, <tt>:micro</tt>, <tt>:nano</tt>, <tt>:pico</tt>, <tt>:femto</tt>
- # * <tt>:format</tt> - Sets the format of the output string (defaults to "%n %u"). The field types are:
- # %u The quantifier (ex.: 'thousand')
- # %n The number
- # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when the argument is invalid.
#
+ # * <tt>:locale</tt> - Sets the locale to be used for formatting
+ # (defaults to current locale).
+ # * <tt>:precision</tt> - Sets the precision of the number
+ # (defaults to 3).
+ # * <tt>:significant</tt> - If +true+, precision will be the #
+ # of significant_digits. If +false+, the # of fractional
+ # digits (defaults to +true+)
+ # * <tt>:separator</tt> - Sets the separator between the
+ # fractional and integer digits (defaults to ".").
+ # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
+ # to "").
+ # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
+ # insignificant zeros after the decimal separator (defaults to
+ # +true+)
+ # * <tt>:units</tt> - A Hash of unit quantifier names. Or a
+ # string containing an i18n scope where to find this hash. It
+ # might have the following keys:
+ # * *integers*: <tt>:unit</tt>, <tt>:ten</tt>,
+ # *<tt>:hundred</tt>, <tt>:thousand</tt>, <tt>:million</tt>,
+ # *<tt>:billion</tt>, <tt>:trillion</tt>,
+ # *<tt>:quadrillion</tt>
+ # * *fractionals*: <tt>:deci</tt>, <tt>:centi</tt>,
+ # *<tt>:mili</tt>, <tt>:micro</tt>, <tt>:nano</tt>,
+ # *<tt>:pico</tt>, <tt>:femto</tt>
+ # * <tt>:format</tt> - Sets the format of the output string
+ # (defaults to "%n %u"). The field types are:
+ # * %u - The quantifier (ex.: 'thousand')
+ # * %n - The number
+ # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
+ # the argument is invalid.
#
# ==== Examples
+ #
# number_to_human(123) # => "123"
# number_to_human(1234) # => "1.23 Thousand"
# number_to_human(12345) # => "12.3 Thousand"
@@ -414,8 +482,9 @@ module ActionView
# :separator => ',',
# :significant => false) # => "1,2 Million"
#
- # Unsignificant zeros after the decimal separator are stripped out by default (set
- # <tt>:strip_insignificant_zeros</tt> to +false+ to change that):
+ # Non-significant zeros after the decimal separator are stripped
+ # out by default (set <tt>:strip_insignificant_zeros</tt> to
+ # +false+ to change that):
# number_to_human(12345012345, :significant_digits => 6) # => "12.345 Billion"
# number_to_human(500000000, :precision => 5) # => "500 Million"
#
diff --git a/actionpack/lib/action_view/helpers/tags/collection_helpers.rb b/actionpack/lib/action_view/helpers/tags/collection_helpers.rb
index 6a1479069f..4e33e79a36 100644
--- a/actionpack/lib/action_view/helpers/tags/collection_helpers.rb
+++ b/actionpack/lib/action_view/helpers/tags/collection_helpers.rb
@@ -49,7 +49,7 @@ module ActionView
accept = if current_value.respond_to?(:call)
current_value.call(item)
else
- Array(current_value).include?(value)
+ Array(current_value).map(&:to_s).include?(value.to_s)
end
if accept
diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb
index fffc37ce9e..698f4434ba 100644
--- a/actionpack/lib/action_view/helpers/text_helper.rb
+++ b/actionpack/lib/action_view/helpers/text_helper.rb
@@ -90,7 +90,7 @@ module ActionView
# Highlights one or more +phrases+ everywhere in +text+ by inserting it into
# a <tt>:highlighter</tt> string. The highlighter can be specialized by passing <tt>:highlighter</tt>
- # as a single-quoted string with \1 where the phrase is to be inserted (defaults to
+ # as a single-quoted string with <tt>\1</tt> where the phrase is to be inserted (defaults to
# '<mark>\1</mark>')
#
# ==== Examples
@@ -108,7 +108,9 @@ module ActionView
#
# You can still use <tt>highlight</tt> with the old API that accepts the
# +highlighter+ as its optional third parameter:
- # highlight('You searched for: rails', 'rails', '<a href="search?q=\1">\1</a>') # => You searched for: <a href="search?q=rails">rails</a>
+ #
+ # highlight('You searched for: rails', 'rails', '<a href="search?q=\1">\1</a>')
+ # # => You searched for: <a href="search?q=rails">rails</a>
def highlight(text, phrases, *args)
options = args.extract_options!
unless args.empty?
diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb
index fd06bfa2a8..8171bea8ed 100644
--- a/actionpack/lib/action_view/helpers/translation_helper.rb
+++ b/actionpack/lib/action_view/helpers/translation_helper.rb
@@ -63,6 +63,9 @@ module ActionView
alias :t :translate
# Delegates to <tt>I18n.localize</tt> with no additional functionality.
+ #
+ # See http://rubydoc.info/github/svenfuchs/i18n/master/I18n/Backend/Base:localize
+ # for more information.
def localize(*args)
I18n.localize(*args)
end
diff --git a/actionpack/lib/action_view/template/resolver.rb b/actionpack/lib/action_view/template/resolver.rb
index 8ea2e5bfe4..08155af013 100644
--- a/actionpack/lib/action_view/template/resolver.rb
+++ b/actionpack/lib/action_view/template/resolver.rb
@@ -1,5 +1,6 @@
require "pathname"
require "active_support/core_ext/class"
+require "active_support/core_ext/class/attribute_accessors"
require "action_view/template"
module ActionView
diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb
index 22ba047328..ba06bcae51 100644
--- a/actionpack/test/abstract_unit.rb
+++ b/actionpack/test/abstract_unit.rb
@@ -1,11 +1,5 @@
require File.expand_path('../../../load_paths', __FILE__)
-lib = File.expand_path("#{File.dirname(__FILE__)}/../lib")
-$:.unshift(lib) unless $:.include?('lib') || $:.include?(lib)
-
-activemodel_path = File.expand_path('../../../activemodel/lib', __FILE__)
-$:.unshift(activemodel_path) if File.directory?(activemodel_path) && !$:.include?(activemodel_path)
-
$:.unshift(File.dirname(__FILE__) + '/lib')
$:.unshift(File.dirname(__FILE__) + '/fixtures/helpers')
$:.unshift(File.dirname(__FILE__) + '/fixtures/alternate_helpers')
diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb
index 877b91b563..fb41dcb33a 100644
--- a/actionpack/test/controller/integration_test.rb
+++ b/actionpack/test/controller/integration_test.rb
@@ -405,6 +405,15 @@ class IntegrationProcessTest < ActionDispatch::IntegrationTest
end
end
+ def test_request_with_bad_format
+ with_test_route_set do
+ xhr :get, '/get.php'
+ assert_equal 406, status
+ assert_response 406
+ assert_response :not_acceptable
+ end
+ end
+
def test_get_with_query_string
with_test_route_set do
get '/get_with_params?foo=bar'
@@ -606,3 +615,83 @@ class EnvironmentFilterIntegrationTest < ActionDispatch::IntegrationTest
assert_equal '[FILTERED]', request.filtered_env['rack.request.form_vars']
end
end
+
+class UrlOptionsIntegrationTest < ActionDispatch::IntegrationTest
+ class FooController < ActionController::Base
+ def index
+ render :text => "foo#index"
+ end
+
+ def show
+ render :text => "foo#show"
+ end
+
+ def edit
+ render :text => "foo#show"
+ end
+ end
+
+ class BarController < ActionController::Base
+ def default_url_options
+ { :host => "bar.com" }
+ end
+
+ def index
+ render :text => "foo#index"
+ end
+ end
+
+ def self.routes
+ @routes ||= ActionDispatch::Routing::RouteSet.new
+ end
+
+ def self.call(env)
+ routes.call(env)
+ end
+
+ def app
+ self.class
+ end
+
+ routes.draw do
+ default_url_options :host => "foo.com"
+
+ scope :module => "url_options_integration_test" do
+ get "/foo" => "foo#index", :as => :foos
+ get "/foo/:id" => "foo#show", :as => :foo
+ get "/foo/:id/edit" => "foo#edit", :as => :edit_foo
+ get "/bar" => "bar#index", :as => :bars
+ end
+ end
+
+ test "session uses default url options from routes" do
+ assert_equal "http://foo.com/foo", foos_url
+ end
+
+ test "current host overrides default url options from routes" do
+ get "/foo"
+ assert_response :success
+ assert_equal "http://www.example.com/foo", foos_url
+ end
+
+ test "controller can override default url options from request" do
+ get "/bar"
+ assert_response :success
+ assert_equal "http://bar.com/foo", foos_url
+ end
+
+ test "test can override default url options" do
+ default_url_options[:host] = "foobar.com"
+ assert_equal "http://foobar.com/foo", foos_url
+
+ get "/bar"
+ assert_response :success
+ assert_equal "http://foobar.com/foo", foos_url
+ end
+
+ test "current request path parameters are recalled" do
+ get "/foo/1"
+ assert_response :success
+ assert_equal "/foo/1/edit", url_for(:action => 'edit', :only_path => true)
+ end
+end
diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb
index ac056319fc..bdcd5561a8 100644
--- a/actionpack/test/controller/mime_responds_test.rb
+++ b/actionpack/test/controller/mime_responds_test.rb
@@ -207,8 +207,9 @@ class RespondToControllerTest < ActionController::TestCase
get :html_or_xml
assert_equal 'HTML', @response.body
- get :just_xml
- assert_response 406
+ assert_raises(ActionController::UnknownFormat) do
+ get :just_xml
+ end
end
def test_all
@@ -239,8 +240,10 @@ class RespondToControllerTest < ActionController::TestCase
assert_equal 'HTML', @response.body
@request.accept = "text/javascript, text/html"
- xhr :get, :just_xml
- assert_response 406
+
+ assert_raises(ActionController::UnknownFormat) do
+ xhr :get, :just_xml
+ end
end
def test_json_or_yaml_with_leading_star_star
@@ -495,9 +498,9 @@ class RespondToControllerTest < ActionController::TestCase
end
def test_invalid_format
- get :using_defaults, :format => "invalidformat"
- assert_equal " ", @response.body
- assert_equal "text/html", @response.content_type
+ assert_raises(ActionController::UnknownFormat) do
+ get :using_defaults, :format => "invalidformat"
+ end
end
end
@@ -701,12 +704,14 @@ class RespondWithControllerTest < ActionController::TestCase
def test_not_acceptable
@request.accept = "application/xml"
- get :using_resource_with_block
- assert_equal 406, @response.status
+ assert_raises(ActionController::UnknownFormat) do
+ get :using_resource_with_block
+ end
@request.accept = "text/javascript"
- get :using_resource_with_overwrite_block
- assert_equal 406, @response.status
+ assert_raises(ActionController::UnknownFormat) do
+ get :using_resource_with_overwrite_block
+ end
end
def test_using_resource_for_post_with_html_redirects_on_success
@@ -984,8 +989,9 @@ class RespondWithControllerTest < ActionController::TestCase
def test_clear_respond_to
@controller = InheritedRespondWithController.new
@request.accept = "text/html"
- get :index
- assert_equal 406, @response.status
+ assert_raises(ActionController::UnknownFormat) do
+ get :index
+ end
end
def test_first_in_respond_to_has_higher_priority
diff --git a/actionpack/test/controller/new_base/render_text_test.rb b/actionpack/test/controller/new_base/render_text_test.rb
index e0b38b29fa..f8d02e8b6c 100644
--- a/actionpack/test/controller/new_base/render_text_test.rb
+++ b/actionpack/test/controller/new_base/render_text_test.rb
@@ -65,7 +65,7 @@ module RenderText
class RenderTextTest < Rack::TestCase
describe "Rendering text using render :text"
- test "rendering text from a action with default options renders the text with the layout" do
+ test "rendering text from an action with default options renders the text with the layout" do
with_routing do |set|
set.draw { get ':controller', :action => 'index' }
@@ -75,7 +75,7 @@ module RenderText
end
end
- test "rendering text from a action with default options renders the text without the layout" do
+ test "rendering text from an action with default options renders the text without the layout" do
with_routing do |set|
set.draw { get ':controller', :action => 'index' }
diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb
index 10f62dad65..3d58c02338 100644
--- a/actionpack/test/controller/render_test.rb
+++ b/actionpack/test/controller/render_test.rb
@@ -102,12 +102,12 @@ class TestController < ActionController::Base
end
def conditional_hello_with_expires_in_with_public_with_more_keys
- expires_in 1.minute, :public => true, 'max-stale' => 5.hours
+ expires_in 1.minute, :public => true, 's-maxage' => 5.hours
render :action => 'hello_world'
end
def conditional_hello_with_expires_in_with_public_with_more_keys_old_syntax
- expires_in 1.minute, :public => true, :private => nil, 'max-stale' => 5.hours
+ expires_in 1.minute, :public => true, :private => nil, 's-maxage' => 5.hours
render :action => 'hello_world'
end
@@ -505,6 +505,14 @@ class TestController < ActionController::Base
render :text => "hello world!"
end
+ def head_created
+ head :created
+ end
+
+ def head_created_with_application_json_content_type
+ head :created, :content_type => "application/json"
+ end
+
def head_with_location_header
head :location => "/foo"
end
@@ -1177,6 +1185,19 @@ class RenderTest < ActionController::TestCase
assert_equal "<html>\n <p>Hello</p>\n</html>\n", @response.body
end
+ def test_head_created
+ post :head_created
+ assert_blank @response.body
+ assert_response :created
+ end
+
+ def test_head_created_with_application_json_content_type
+ post :head_created_with_application_json_content_type
+ assert_blank @response.body
+ assert_equal "application/json", @response.content_type
+ assert_response :created
+ end
+
def test_head_with_location_header
get :head_with_location_header
assert_blank @response.body
@@ -1478,12 +1499,12 @@ class ExpiresInRenderTest < ActionController::TestCase
def test_expires_in_header_with_additional_headers
get :conditional_hello_with_expires_in_with_public_with_more_keys
- assert_equal "max-age=60, public, max-stale=18000", @response.headers["Cache-Control"]
+ assert_equal "max-age=60, public, s-maxage=18000", @response.headers["Cache-Control"]
end
def test_expires_in_old_syntax
get :conditional_hello_with_expires_in_with_public_with_more_keys_old_syntax
- assert_equal "max-age=60, public, max-stale=18000", @response.headers["Cache-Control"]
+ assert_equal "max-age=60, public, s-maxage=18000", @response.headers["Cache-Control"]
end
def test_expires_now
diff --git a/actionpack/test/dispatch/mount_test.rb b/actionpack/test/dispatch/mount_test.rb
index f7a746120e..536e35ab2e 100644
--- a/actionpack/test/dispatch/mount_test.rb
+++ b/actionpack/test/dispatch/mount_test.rb
@@ -37,6 +37,11 @@ class TestRoutingMount < ActionDispatch::IntegrationTest
assert_equal "/sprockets -- /omg", response.body
end
+ def test_mounting_works_with_nested_script_name
+ get "/foo/sprockets/omg", {}, 'SCRIPT_NAME' => '/foo', 'PATH_INFO' => '/sprockets/omg'
+ assert_equal "/foo/sprockets -- /omg", response.body
+ end
+
def test_mounting_works_with_scope
get "/its_a/sprocket/omg"
assert_equal "/its_a/sprocket -- /omg", response.body
diff --git a/actionpack/test/dispatch/routing_assertions_test.rb b/actionpack/test/dispatch/routing_assertions_test.rb
index e953029456..517354ae58 100644
--- a/actionpack/test/dispatch/routing_assertions_test.rb
+++ b/actionpack/test/dispatch/routing_assertions_test.rb
@@ -47,7 +47,7 @@ class RoutingAssertionsTest < ActionController::TestCase
def test_assert_recognizes_with_extras
assert_recognizes({ :controller => 'articles', :action => 'index', :page => '1' }, '/articles', { :page => '1' })
end
-
+
def test_assert_recognizes_with_method
assert_recognizes({ :controller => 'articles', :action => 'create' }, { :path => '/articles', :method => :post })
assert_recognizes({ :controller => 'articles', :action => 'update', :id => '1' }, { :path => '/articles/1', :method => :put })
@@ -57,7 +57,7 @@ class RoutingAssertionsTest < ActionController::TestCase
assert_raise(ActionController::RoutingError) do
assert_recognizes({ :controller => 'secure_articles', :action => 'index' }, 'http://test.host/secure/articles')
end
- assert_recognizes({ :controller => 'secure_articles', :action => 'index' }, 'https://test.host/secure/articles')
+ assert_recognizes({ :controller => 'secure_articles', :action => 'index', :protocol => 'https://' }, 'https://test.host/secure/articles')
end
def test_assert_recognizes_with_block_constraint
@@ -90,7 +90,7 @@ class RoutingAssertionsTest < ActionController::TestCase
assert_raise(ActionController::RoutingError) do
assert_routing('http://test.host/secure/articles', { :controller => 'secure_articles', :action => 'index' })
end
- assert_routing('https://test.host/secure/articles', { :controller => 'secure_articles', :action => 'index' })
+ assert_routing('https://test.host/secure/articles', { :controller => 'secure_articles', :action => 'index', :protocol => 'https://' })
end
def test_assert_routing_with_block_constraint
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index c8e1b34b99..3cec816f1c 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -829,6 +829,15 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
assert_equal original_options, options
end
+ def test_url_for_does_not_modify_controller
+ controller = '/projects'
+ options = {:controller => controller, :action => 'status', :only_path => true}
+ url = url_for(options)
+
+ assert_equal '/projects/status', url
+ assert_equal '/projects', controller
+ end
+
# tests the arguments modification free version of define_hash_access
def test_named_route_with_no_side_effects
original_options = { :host => 'test.host' }
@@ -2538,3 +2547,104 @@ class TestConstraintsAccessingParameters < ActionDispatch::IntegrationTest
assert_equal "bar", @request.params[:bar]
end
end
+
+class TestOptimizedNamedRoutes < ActionDispatch::IntegrationTest
+ Routes = ActionDispatch::Routing::RouteSet.new.tap do |app|
+ app.draw do
+ ok = lambda { |env| [200, { 'Content-Type' => 'text/plain' }, []] }
+ get '/foo' => ok, as: :foo
+ end
+ end
+
+ include Routes.url_helpers
+ def app; Routes end
+
+ test 'enabled when not mounted and default_url_options is empty' do
+ assert Routes.url_helpers.optimize_routes_generation?
+ end
+
+ test 'named route called as singleton method' do
+ assert_equal '/foo', Routes.url_helpers.foo_path
+ end
+
+ test 'named route called on included module' do
+ assert_equal '/foo', foo_path
+ end
+end
+
+class TestNamedRouteUrlHelpers < ActionDispatch::IntegrationTest
+ class CategoriesController < ActionController::Base
+ def show
+ render :text => "categories#show"
+ end
+ end
+
+ class ProductsController < ActionController::Base
+ def show
+ render :text => "products#show"
+ end
+ end
+
+ Routes = ActionDispatch::Routing::RouteSet.new.tap do |app|
+ app.draw do
+ scope :module => "test_named_route_url_helpers" do
+ get "/categories/:id" => 'categories#show', :as => :category
+ get "/products/:id" => 'products#show', :as => :product
+ end
+ end
+ end
+
+ def app; Routes end
+
+ include Routes.url_helpers
+
+ test "url helpers do not ignore nil parameters when using non-optimized routes" do
+ Routes.stubs(:optimize_routes_generation?).returns(false)
+
+ get "/categories/1"
+ assert_response :success
+ assert_raises(ActionController::RoutingError) { product_path(nil) }
+ end
+end
+
+class TestUrlConstraints < ActionDispatch::IntegrationTest
+ Routes = ActionDispatch::Routing::RouteSet.new.tap do |app|
+ app.draw do
+ ok = lambda { |env| [200, { 'Content-Type' => 'text/plain' }, []] }
+
+ constraints :subdomain => 'admin' do
+ get '/' => ok, :as => :admin_root
+ end
+
+ scope :constraints => { :protocol => 'https://' } do
+ get '/' => ok, :as => :secure_root
+ end
+
+ get '/' => ok, :as => :alternate_root, :constraints => { :port => 8080 }
+ end
+ end
+
+ include Routes.url_helpers
+ def app; Routes end
+
+ test "constraints are copied to defaults when using constraints method" do
+ assert_equal 'http://admin.example.com/', admin_root_url
+
+ get 'http://admin.example.com/'
+ assert_response :success
+ end
+
+ test "constraints are copied to defaults when using scope constraints hash" do
+ assert_equal 'https://www.example.com/', secure_root_url
+
+ get 'https://www.example.com/'
+ assert_response :success
+ end
+
+ test "constraints are copied to defaults when using route constraints hash" do
+ assert_equal 'http://www.example.com:8080/', alternate_root_url
+
+ get 'http://www.example.com:8080/'
+ assert_response :success
+ end
+end
diff --git a/actionpack/test/dispatch/url_generation_test.rb b/actionpack/test/dispatch/url_generation_test.rb
index 985ff2e81a..e56e8ddc57 100644
--- a/actionpack/test/dispatch/url_generation_test.rb
+++ b/actionpack/test/dispatch/url_generation_test.rb
@@ -3,7 +3,7 @@ require 'abstract_unit'
module TestUrlGeneration
class WithMountPoint < ActionDispatch::IntegrationTest
Routes = ActionDispatch::Routing::RouteSet.new
- Routes.draw { get "/foo", :to => "my_route_generating#index", :as => :foo }
+ include Routes.url_helpers
class ::MyRouteGeneratingController < ActionController::Base
include Routes.url_helpers
@@ -12,7 +12,11 @@ module TestUrlGeneration
end
end
- include Routes.url_helpers
+ Routes.draw do
+ get "/foo", :to => "my_route_generating#index", :as => :foo
+
+ mount MyRouteGeneratingController.action(:index), at: '/bar'
+ end
def _routes
Routes
@@ -30,11 +34,16 @@ module TestUrlGeneration
assert_equal "/bar/foo", foo_path(:script_name => "/bar")
end
- test "the request's SCRIPT_NAME takes precedence over the routes'" do
+ test "the request's SCRIPT_NAME takes precedence over the route" do
get "/foo", {}, 'SCRIPT_NAME' => "/new", 'action_dispatch.routes' => Routes
assert_equal "/new/foo", response.body
end
+ test "the request's SCRIPT_NAME wraps the mounted app's" do
+ get '/new/bar/foo', {}, 'SCRIPT_NAME' => '/new', 'PATH_INFO' => '/bar/foo', 'action_dispatch.routes' => Routes
+ assert_equal "/new/bar/foo", response.body
+ end
+
test "handling http protocol with https set" do
https!
assert_equal "http://www.example.com/foo", foo_url(:protocol => "http")
diff --git a/actionpack/test/template/form_collections_helper_test.rb b/actionpack/test/template/form_collections_helper_test.rb
index 4d878635ef..c73e80ed88 100644
--- a/actionpack/test/template/form_collections_helper_test.rb
+++ b/actionpack/test/template/form_collections_helper_test.rb
@@ -195,6 +195,15 @@ class FormCollectionsHelperTest < ActionView::TestCase
assert_no_select 'input[type=checkbox][value=2][checked=checked]'
end
+ test 'collection check boxes accepts selected string values as :checked option' do
+ collection = (1..3).map{|i| [i, "Category #{i}"] }
+ with_collection_check_boxes :user, :category_ids, collection, :first, :last, :checked => ['1', '3']
+
+ assert_select 'input[type=checkbox][value=1][checked=checked]'
+ assert_select 'input[type=checkbox][value=3][checked=checked]'
+ assert_no_select 'input[type=checkbox][value=2][checked=checked]'
+ end
+
test 'collection check boxes accepts a single checked value' do
collection = (1..3).map{|i| [i, "Category #{i}"] }
with_collection_check_boxes :user, :category_ids, collection, :first, :last, :checked => 3
diff --git a/actionpack/test/ts_isolated.rb b/actionpack/test/ts_isolated.rb
index ae2a0c95f6..595b4018e9 100644
--- a/actionpack/test/ts_isolated.rb
+++ b/actionpack/test/ts_isolated.rb
@@ -1,6 +1,3 @@
-$:.unshift(File.dirname(__FILE__))
-$:.unshift(File.dirname(__FILE__) + '/../../activesupport/lib')
-
require 'minitest/autorun'
require 'rbconfig'
require 'abstract_unit'
diff --git a/activemodel/README.rdoc b/activemodel/README.rdoc
index 1fd75141f8..0c7089598c 100644
--- a/activemodel/README.rdoc
+++ b/activemodel/README.rdoc
@@ -135,6 +135,16 @@ behavior out of the box:
pattern in a Rails App and take advantage of all the standard observer
functions.
+ class PersonObserver < ActiveModel::Observer
+ def after_create(person)
+ person.logger.info("New person added!")
+ end
+
+ def after_destroy(person)
+ person.logger.warn("Person with an id of #{person.id} was destroyed!")
+ end
+ end
+
{Learn more}[link:classes/ActiveModel/Observer.html]
* Making objects serializable
diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb
index 2586147a20..ded1b752df 100644
--- a/activemodel/lib/active_model.rb
+++ b/activemodel/lib/active_model.rb
@@ -21,8 +21,6 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++
-activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__)
-$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path)
require 'active_support'
require 'active_model/version'
@@ -59,5 +57,6 @@ module ActiveModel
end
end
-require 'active_support/i18n'
-I18n.load_path << File.dirname(__FILE__) + '/active_model/locale/en.yml'
+ActiveSupport.on_load(:i18n) do
+ I18n.load_path << File.dirname(__FILE__) + '/active_model/locale/en.yml'
+end
diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb
index 97a83e58af..25d5d84ce6 100644
--- a/activemodel/lib/active_model/attribute_methods.rb
+++ b/activemodel/lib/active_model/attribute_methods.rb
@@ -180,6 +180,18 @@ module ActiveModel
undefine_attribute_methods
end
+
+ # Allows you to make aliases for attributes.
+ #
+ # class Person
+ # attr_accessor :name
+ # alias_attribute :nickname, :name
+ # end
+ #
+ # person = Person.new
+ # person.nickname = "Bob"
+ # person.nickname # => "Bob"
+ # person.name # => "Bob"
def alias_attribute(new_name, old_name)
attribute_method_matchers.each do |matcher|
matcher_new = matcher.method_name(new_name).to_s
diff --git a/activemodel/lib/active_model/serialization.rb b/activemodel/lib/active_model/serialization.rb
index 4403ef060b..00cd05b7ef 100644
--- a/activemodel/lib/active_model/serialization.rb
+++ b/activemodel/lib/active_model/serialization.rb
@@ -87,8 +87,8 @@ module ActiveModel
Array(options[:methods]).each { |m| hash[m.to_s] = send(m) if respond_to?(m) }
serializable_add_includes(options) do |association, records, opts|
- hash[association.to_s] = if records.is_a?(Enumerable)
- records.map { |a| a.serializable_hash(opts) }
+ hash[association.to_s] = if records.respond_to?(:to_ary)
+ records.to_ary.map { |a| a.serializable_hash(opts) }
else
records.serializable_hash(opts)
end
diff --git a/activemodel/lib/active_model/serializers/xml.rb b/activemodel/lib/active_model/serializers/xml.rb
index 5084298210..b78f1ff3f3 100644
--- a/activemodel/lib/active_model/serializers/xml.rb
+++ b/activemodel/lib/active_model/serializers/xml.rb
@@ -115,7 +115,9 @@ module ActiveModel
merged_options = opts.merge(options.slice(:builder, :indent))
merged_options[:skip_instruct] = true
- if records.is_a?(Enumerable)
+ if records.respond_to?(:to_ary)
+ records = records.to_ary
+
tag = ActiveSupport::XmlMini.rename_key(association.to_s, options)
type = options[:skip_types] ? { } : {:type => "array"}
association_name = association.to_s.singularize
diff --git a/activemodel/test/cases/helper.rb b/activemodel/test/cases/helper.rb
index 4347b17cbc..7d6f11b5a5 100644
--- a/activemodel/test/cases/helper.rb
+++ b/activemodel/test/cases/helper.rb
@@ -1,8 +1,5 @@
require File.expand_path('../../../../load_paths', __FILE__)
-lib = File.expand_path("#{File.dirname(__FILE__)}/../../lib")
-$:.unshift(lib) unless $:.include?('lib') || $:.include?(lib)
-
require 'config'
require 'active_model'
require 'active_support/core_ext/string/access'
diff --git a/activemodel/test/cases/serialization_test.rb b/activemodel/test/cases/serialization_test.rb
index 66b18d65e5..d2ba9fd95d 100644
--- a/activemodel/test/cases/serialization_test.rb
+++ b/activemodel/test/cases/serialization_test.rb
@@ -105,6 +105,24 @@ class SerializationTest < ActiveModel::TestCase
assert_equal expected, @user.serializable_hash(:include => :friends)
end
+ class FriendList
+ def initialize(friends)
+ @friends = friends
+ end
+
+ def to_ary
+ @friends
+ end
+ end
+
+ def test_include_option_with_ary
+ @user.friends = FriendList.new(@user.friends)
+ expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David",
+ "friends"=>[{"name"=>'Joe', "email"=>'joe@example.com', "gender"=>'male'},
+ {"name"=>'Sue', "email"=>'sue@example.com', "gender"=>'female'}]}
+ assert_equal expected, @user.serializable_hash(:include => :friends)
+ end
+
def test_multiple_includes
expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David",
"address"=>{"street"=>"123 Lane", "city"=>"Springfield", "state"=>"CA", "zip"=>11111},
diff --git a/activemodel/test/cases/serializers/xml_serialization_test.rb b/activemodel/test/cases/serializers/xml_serialization_test.rb
index 38aecf51ff..5fa227e0e0 100644
--- a/activemodel/test/cases/serializers/xml_serialization_test.rb
+++ b/activemodel/test/cases/serializers/xml_serialization_test.rb
@@ -188,6 +188,23 @@ class XmlSerializationTest < ActiveModel::TestCase
assert_match %r{<friend type="Contact">}, xml
end
+ class FriendList
+ def initialize(friends)
+ @friends = friends
+ end
+
+ def to_ary
+ @friends
+ end
+ end
+
+ test "include option with ary" do
+ @contact.friends = FriendList.new(@contact.friends)
+ xml = @contact.to_xml :include => :friends, :indent => 0
+ assert_match %r{<friends type="array">}, xml
+ assert_match %r{<friend type="Contact">}, xml
+ end
+
test "multiple includes" do
xml = @contact.to_xml :indent => 0, :skip_instruct => true, :include => [ :address, :friends ]
assert xml.include?(@contact.address.to_xml(:indent => 0, :skip_instruct => true))
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index a661a44f1f..31886c8212 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,5 +1,11 @@
## Rails 4.0.0 (unreleased) ##
+* `mysql` and `mysql2` connections will set `SQL_MODE=STRICT_ALL_TABLES` by
+ default to avoid silent data loss. This can be disabled by specifying
+ `strict: false` in your `database.yml`.
+
+ *Michael Pearson*
+
* Added default order to `first` to assure consistent results among
diferent database engines. Introduced `take` as a replacement to
the old behavior of `first`.
diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb
index ed26b4899f..210820062b 100644
--- a/activerecord/lib/active_record.rb
+++ b/activerecord/lib/active_record.rb
@@ -22,7 +22,6 @@
#++
require 'active_support'
-require 'active_support/i18n'
require 'active_model'
require 'arel'
require 'active_record_deprecated_finders'
@@ -145,4 +144,6 @@ ActiveSupport.on_load(:active_record) do
Arel::Table.engine = self
end
-I18n.load_path << File.dirname(__FILE__) + '/active_record/locale/en.yml'
+ActiveSupport.on_load(:i18n) do
+ I18n.load_path << File.dirname(__FILE__) + '/active_record/locale/en.yml'
+end
diff --git a/activerecord/lib/active_record/aggregations.rb b/activerecord/lib/active_record/aggregations.rb
index a4db627535..c7a329d74d 100644
--- a/activerecord/lib/active_record/aggregations.rb
+++ b/activerecord/lib/active_record/aggregations.rb
@@ -71,7 +71,7 @@ module ActiveRecord
# Now it's possible to access attributes from the database through the value objects instead. If
# you choose to name the composition the same as the attribute's name, it will be the only way to
# access that attribute. That's the case with our +balance+ attribute. You interact with the value
- # objects just like you would any other attribute, though:
+ # objects just like you would with any other attribute:
#
# customer.balance = Money.new(20) # sets the Money value object and the attribute
# customer.balance # => Money value object
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb
index 14aa557b6c..00321ec860 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -16,12 +16,6 @@ module ActiveRecord
# If you need to work on all current children, new and existing records,
# +load_target+ and the +loaded+ flag are your friends.
class CollectionAssociation < Association #:nodoc:
- attr_reader :proxy
-
- def initialize(owner, reflection)
- super
- @proxy = CollectionProxy.new(self)
- end
# Implements the reader method, e.g. foo.items for Foo.has_many :items
def reader(force_reload = false)
@@ -31,7 +25,7 @@ module ActiveRecord
reload
end
- proxy
+ CollectionProxy.new(self)
end
# Implements the writer method, e.g. foo.items= for Foo.has_many :items
diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb
index 261a829281..cf4cc98f38 100644
--- a/activerecord/lib/active_record/associations/collection_proxy.rb
+++ b/activerecord/lib/active_record/associations/collection_proxy.rb
@@ -33,14 +33,7 @@ module ActiveRecord
#
# is computed directly through SQL and does not trigger by itself the
# instantiation of the actual post records.
- class CollectionProxy # :nodoc:
- alias :proxy_extend :extend
-
- instance_methods.each { |m| undef_method m unless m.to_s =~ /^(?:nil\?|send|object_id|to_a)$|^__|^respond_to|proxy_/ }
-
- delegate :group, :order, :limit, :joins, :where, :preload, :eager_load, :includes, :from,
- :lock, :readonly, :having, :pluck, :to => :scoped
-
+ class CollectionProxy < Relation # :nodoc:
delegate :target, :load_target, :loaded?, :to => :@association
delegate :select, :find, :first, :last,
@@ -52,7 +45,8 @@ module ActiveRecord
def initialize(association)
@association = association
- Array(association.options[:extend]).each { |ext| proxy_extend(ext) }
+ super association.klass, association.klass.arel_table
+ merge! association.scoped
end
alias_method :new, :build
@@ -61,54 +55,28 @@ module ActiveRecord
@association
end
- def scoped(options = nil)
- association = @association
- scope = association.scoped
-
- scope.extending! do
- define_method(:proxy_association) { association }
- end
- scope.merge!(options) if options
- scope
+ # We don't want this object to be put on the scoping stack, because
+ # that could create an infinite loop where we call an @association
+ # method, which gets the current scope, which is this object, which
+ # delegates to @association, and so on.
+ def scoping
+ @association.scoped.scoping { yield }
end
- def respond_to?(name, include_private = false)
- super ||
- (load_target && target.respond_to?(name, include_private)) ||
- proxy_association.klass.respond_to?(name, include_private)
+ def spawn
+ scoped
end
- def method_missing(method, *args, &block)
- match = DynamicMatchers::Method.match(self, method)
- if match && match.is_a?(DynamicMatchers::Instantiator)
- scoped.send(method, *args) do |r|
- proxy_association.send :set_owner_attributes, r
- proxy_association.send :add_to_target, r
- yield(r) if block_given?
- end
-
- elsif target.respond_to?(method) || (!proxy_association.klass.respond_to?(method) && Class.respond_to?(method))
- if load_target
- if target.respond_to?(method)
- target.send(method, *args, &block)
- else
- begin
- super
- rescue NoMethodError => e
- raise e, e.message.sub(/ for #<.*$/, " via proxy for #{target}")
- end
- end
- end
+ def scoped(options = nil)
+ association = @association
- else
- scoped.readonly(nil).public_send(method, *args, &block)
+ super.extending! do
+ define_method(:proxy_association) { association }
end
end
- # Forwards <tt>===</tt> explicitly to the \target because the instance method
- # removal above doesn't catch it. Loads the \target if needed.
- def ===(other)
- other === load_target
+ def ==(other)
+ load_target == other
end
def to_ary
@@ -130,19 +98,6 @@ module ActiveRecord
proxy_association.reload
self
end
-
- # Define array public methods because we know it should be invoked over
- # the target, so we can have a performance improvement using those methods
- # in association collections
- Array.public_instance_methods.each do |m|
- unless method_defined?(m)
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
- def #{m}(*args, &block)
- target.public_send(:#{m}, *args, &block) if load_target
- end
- RUBY
- end
- end
end
end
end
diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb
index 3005bef092..d545e7799d 100644
--- a/activerecord/lib/active_record/autosave_association.rb
+++ b/activerecord/lib/active_record/autosave_association.rb
@@ -350,7 +350,7 @@ module ActiveRecord
end
records_to_destroy.each do |record|
- association.proxy.destroy(record)
+ association.destroy(record)
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
index 44ac37c498..6f9f0399db 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
@@ -34,6 +34,7 @@ module ActiveRecord
when Numeric then value.to_s
when Date, Time then "'#{quoted_date(value)}'"
when Symbol then "'#{quote_string(value.to_s)}'"
+ when Class then "'#{value.to_s}'"
else
"'#{quote_string(YAML.dump(value))}'"
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
index f0b6ae2b7d..f17baec722 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -262,7 +262,7 @@ module ActiveRecord
# Adds index options to the indexes hash, keyed by column name
# This is primarily used to track indexes that need to be created after the table
- # === Examples
+ #
# index(:account_id, :name => 'index_projects_on_account_id')
def index(column_name, options = {})
indexes[column_name] = options
@@ -348,7 +348,7 @@ module ActiveRecord
# Adds a new column to the named table.
# See TableDefinition#column for details of the options you can use.
- # ===== Example
+ #
# ====== Creating a simple column
# t.column(:name, :string)
def column(column_name, type, options = {})
@@ -363,7 +363,6 @@ module ActiveRecord
# Adds a new index to the table. +column_name+ can be a single Symbol, or
# an Array of Symbols. See SchemaStatements#add_index
#
- # ===== Examples
# ====== Creating a simple index
# t.index(:name)
# ====== Creating a unique index
@@ -380,7 +379,7 @@ module ActiveRecord
end
# Adds timestamps (+created_at+ and +updated_at+) columns to the table. See SchemaStatements#add_timestamps
- # ===== Example
+ #
# t.timestamps
def timestamps
@base.add_timestamps(@table_name)
@@ -388,7 +387,7 @@ module ActiveRecord
# Changes the column's definition according to the new options.
# See TableDefinition#column for details of the options you can use.
- # ===== Examples
+ #
# t.change(:name, :string, :limit => 80)
# t.change(:description, :text)
def change(column_name, type, options = {})
@@ -396,7 +395,7 @@ module ActiveRecord
end
# Sets a new default value for a column. See SchemaStatements#change_column_default
- # ===== Examples
+ #
# t.change_default(:qualification, 'new')
# t.change_default(:authorized, 1)
def change_default(column_name, default)
@@ -404,7 +403,7 @@ module ActiveRecord
end
# Removes the column(s) from the table definition.
- # ===== Examples
+ #
# t.remove(:qualification)
# t.remove(:qualification, :experience)
def remove(*column_names)
@@ -413,7 +412,6 @@ module ActiveRecord
# Removes the given index from the table.
#
- # ===== Examples
# ====== Remove the index_table_name_on_column in the table_name table
# t.remove_index :column
# ====== Remove the index named index_table_name_on_branch_id in the table_name table
@@ -427,14 +425,14 @@ module ActiveRecord
end
# Removes the timestamp columns (+created_at+ and +updated_at+) from the table.
- # ===== Example
+ #
# t.remove_timestamps
def remove_timestamps
@base.remove_timestamps(@table_name)
end
# Renames a column.
- # ===== Example
+ #
# t.rename(:description, :name)
def rename(column_name, new_column_name)
@base.rename_column(@table_name, column_name, new_column_name)
@@ -442,7 +440,7 @@ module ActiveRecord
# Adds a reference. Optionally adds a +type+ column, if <tt>:polymorphic</tt> option is provided.
# <tt>references</tt> and <tt>belongs_to</tt> are acceptable.
- # ===== Examples
+ #
# t.references(:goat)
# t.references(:goat, :polymorphic => true)
# t.belongs_to(:goat)
@@ -460,7 +458,7 @@ module ActiveRecord
# Removes a reference. Optionally removes a +type+ column.
# <tt>remove_references</tt> and <tt>remove_belongs_to</tt> are acceptable.
- # ===== Examples
+ #
# t.remove_references(:goat)
# t.remove_references(:goat, :polymorphic => true)
# t.remove_belongs_to(:goat)
@@ -475,7 +473,7 @@ module ActiveRecord
alias :remove_belongs_to :remove_references
# Adds a column or columns of a specified type
- # ===== Examples
+ #
# t.string(:goat)
# t.string(:goat, :sheep)
%w( string text integer float decimal datetime timestamp time date binary boolean ).each do |column_type|
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 e7a4f061fd..62b0f51bb2 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -21,7 +21,6 @@ module ActiveRecord
# Checks to see if the table +table_name+ exists on the database.
#
- # === Example
# table_exists?(:developers)
def table_exists?(table_name)
tables.include?(table_name.to_s)
@@ -32,7 +31,6 @@ module ActiveRecord
# Checks to see if an index exists on a table for a given index definition.
#
- # === Examples
# # Check an index exists
# index_exists?(:suppliers, :company_id)
#
@@ -126,7 +124,6 @@ module ActiveRecord
# Set to true to drop the table before creating it.
# Defaults to false.
#
- # ===== Examples
# ====== Add a backend specific option to the generated SQL (MySQL)
# create_table(:suppliers, :options => 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
# generates:
@@ -193,7 +190,6 @@ module ActiveRecord
# Set to true to drop the table before creating it.
# Defaults to false.
#
- # ===== Examples
# ====== Add a backend specific option to the generated SQL (MySQL)
# create_join_table(:assemblies, :parts, :options => 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
# generates:
@@ -215,7 +211,6 @@ module ActiveRecord
# A block for changing columns in +table+.
#
- # === Example
# # change_table() yields a Table instance
# change_table(:suppliers) do |t|
# t.column :name, :string, :limit => 60
@@ -229,7 +224,6 @@ module ActiveRecord
#
# Defaults to false.
#
- # ===== Examples
# ====== Add a column
# change_table(:suppliers) do |t|
# t.column :name, :string, :limit => 60
@@ -288,7 +282,7 @@ module ActiveRecord
end
# Renames a table.
- # ===== Example
+ #
# rename_table('octopuses', 'octopi')
def rename_table(table_name, new_name)
raise NotImplementedError, "rename_table is not implemented"
@@ -308,7 +302,7 @@ module ActiveRecord
end
# Removes the column(s) from the table definition.
- # ===== Examples
+ #
# remove_column(:suppliers, :qualification)
# remove_columns(:suppliers, :qualification, :experience)
def remove_column(table_name, *column_names)
@@ -318,7 +312,7 @@ module ActiveRecord
# Changes the column's definition according to the new options.
# See TableDefinition#column for details of the options you can use.
- # ===== Examples
+ #
# change_column(:suppliers, :name, :string, :limit => 80)
# change_column(:accounts, :description, :text)
def change_column(table_name, column_name, type, options = {})
@@ -326,7 +320,7 @@ module ActiveRecord
end
# Sets a new default value for a column.
- # ===== Examples
+ #
# change_column_default(:suppliers, :qualification, 'new')
# change_column_default(:accounts, :authorized, 1)
# change_column_default(:users, :email, nil)
@@ -335,7 +329,7 @@ module ActiveRecord
end
# Renames a column.
- # ===== Example
+ #
# rename_column(:suppliers, :description, :name)
def rename_column(table_name, column_name, new_column_name)
raise NotImplementedError, "rename_column is not implemented"
@@ -347,8 +341,6 @@ module ActiveRecord
# The index will be named after the table and the column name(s), unless
# you pass <tt>:name</tt> as an option.
#
- # ===== Examples
- #
# ====== Creating a simple index
# add_index(:suppliers, :name)
# generates
@@ -537,7 +529,7 @@ module ActiveRecord
end
# Adds timestamps (created_at and updated_at) columns to the named table.
- # ===== Examples
+ #
# add_timestamps(:suppliers)
def add_timestamps(table_name)
add_column table_name, :created_at, :datetime
@@ -545,7 +537,7 @@ module ActiveRecord
end
# Removes the timestamp columns (created_at and updated_at) from the table definition.
- # ===== Examples
+ #
# remove_timestamps(:suppliers)
def remove_timestamps(table_name)
remove_column table_name, :updated_at
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
index a848838a4e..9794c5663e 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -251,7 +251,7 @@ module ActiveRecord
end
# MysqlAdapter has to free a result after using it, so we use this method to write
- # stuff in a abstract way without concerning ourselves about whether it needs to be
+ # stuff in an abstract way without concerning ourselves about whether it needs to be
# explicitly freed or not.
def execute_and_free(sql, name = nil) #:nodoc:
yield execute(sql, name)
diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb
index 9af8e46120..1933ce2b46 100644
--- a/activerecord/lib/active_record/connection_adapters/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/column.rb
@@ -158,7 +158,7 @@ module ActiveRecord
def value_to_date(value)
if value.is_a?(String)
- return nil if value.empty?
+ return nil if value.blank?
fast_string_to_date(value) || fallback_string_to_date(value)
elsif value.respond_to?(:to_date)
value.to_date
@@ -169,14 +169,14 @@ module ActiveRecord
def string_to_time(string)
return string unless string.is_a?(String)
- return nil if string.empty?
+ return nil if string.blank?
fast_string_to_time(string) || fallback_string_to_time(string)
end
def string_to_dummy_time(string)
return string unless string.is_a?(String)
- return nil if string.empty?
+ return nil if string.blank?
string_to_time "2000-01-01 #{string}"
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
index 92908d9599..350ccce03d 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
@@ -253,6 +253,14 @@ module ActiveRecord
# By default, MySQL 'where id is null' selects the last inserted id.
# Turn this off. http://dev.rubyonrails.org/ticket/6778
variable_assignments = ['SQL_AUTO_IS_NULL=0']
+
+ # Make MySQL reject illegal values rather than truncating or
+ # blanking them. See
+ # http://dev.mysql.com/doc/refman/5.5/en/server-sql-mode.html#sqlmode_strict_all_tables
+ if @config.fetch(:strict, true)
+ variable_assignments << "SQL_MODE='STRICT_ALL_TABLES'"
+ end
+
encoding = @config[:encoding]
# make sure we set the encoding
diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
index 724dbff1f0..0b6734b010 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -53,6 +53,7 @@ module ActiveRecord
# * <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>:reconnect</tt> - Defaults to false (See MySQL documentation: http://dev.mysql.com/doc/refman/5.0/en/auto-reconnect.html).
+ # * <tt>:strict</tt> - Defaults to true. Enable STRICT_ALL_TABLES. (See MySQL documentation: http://dev.mysql.com/doc/refman/5.5/en/server-sql-mode.html)
# * <tt>:sslca</tt> - Necessary to use MySQL with an SSL connection.
# * <tt>:sslkey</tt> - Necessary to use MySQL with an SSL connection.
# * <tt>:sslcert</tt> - Necessary to use MySQL with an SSL connection.
@@ -404,6 +405,13 @@ module ActiveRecord
# By default, MySQL 'where id is null' selects the last inserted id.
# Turn this off. http://dev.rubyonrails.org/ticket/6778
execute("SET SQL_AUTO_IS_NULL=0", :skip_logging)
+
+ # Make MySQL reject illegal values rather than truncating or
+ # blanking them. See
+ # http://dev.mysql.com/doc/refman/5.5/en/server-sql-mode.html#sqlmode_strict_all_tables
+ if @config.fetch(:strict, true)
+ execute("SET SQL_MODE='STRICT_ALL_TABLES'", :skip_logging)
+ end
end
def select(sql, name = nil, binds = [])
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index 9a76c9f617..ed555e4bf6 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -1,6 +1,6 @@
require 'active_support/concern'
require 'active_support/core_ext/hash/indifferent_access'
-require 'active_support/core_ext/object/duplicable'
+require 'active_support/core_ext/object/deep_dup'
require 'thread'
module ActiveRecord
@@ -77,7 +77,7 @@ module ActiveRecord
##
# :singleton-method:
- # Specifies wether or not has_many or has_one association option
+ # Specifies whether or not has_many or has_one association option
# :dependent => :restrict raises an exception. If set to true, the
# ActiveRecord::DeleteRestrictionError exception will be raised
# along with a DEPRECATION WARNING. If set to false, an error would
@@ -166,9 +166,7 @@ module ActiveRecord
# # Instantiates a single new object bypassing mass-assignment security
# User.new({ :first_name => 'Jamie', :is_admin => true }, :without_protection => true)
def initialize(attributes = nil, options = {})
- # TODO: use deep_dup after fixing it to also dup values
- defaults = Hash[self.class.column_defaults.map { |k, v| [k, v.duplicable? ? v.dup : v] }]
- @attributes = self.class.initialize_attributes(defaults)
+ @attributes = self.class.initialize_attributes(self.class.column_defaults.deep_dup)
@columns_hash = self.class.column_types.dup
init_internals
@@ -206,14 +204,35 @@ module ActiveRecord
self
end
-
+
+ ##
+ # :method: clone
+ # Identical to Ruby's clone method. This is a "shallow" copy. Be warned that your attributes are not copied.
+ # That means that modifying attributes of the clone will modify the original, since they will both point to the
+ # same attributes hash. If you need a copy of your attributes hash, please use the #dup method.
+ #
+ # user = User.first
+ # new_user = user.clone
+ # user.name # => "Bob"
+ # new_user.name = "Joe"
+ # user.name # => "Joe"
+ #
+ # user.object_id == new_user.object_id # => false
+ # user.name.object_id == new_user.name.object_id # => true
+ #
+ # user.name.object_id == user.dup.name.object_id # => false
+
+ ##
+ # :method: dup
# Duped objects have no id assigned and are treated as new records. Note
# that this is a "shallow" copy as it copies the object's attributes
# only, not its associations. The extent of a "deep" copy is application
# specific and is therefore left to the application to implement according
# to its need.
# The dup method does not preserve the timestamps (created|updated)_(at|on).
- def initialize_dup(other)
+
+ ##
+ def initialize_dup(other) # :nodoc:
cloned_attributes = other.clone_attributes(:read_attribute_before_type_cast)
self.class.initialize_attributes(cloned_attributes)
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index a01e2f74ff..c630af59f0 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -372,19 +372,23 @@ module ActiveRecord
#
# Any fixture labeled "DEFAULTS" is safely ignored.
class Fixtures
+ #--
+ # NOTE: an instance of Fixtures can be called fixture_set, it is normally stored in a single YAML file and possibly in a folder with the same name.
+ #++
MAX_ID = 2 ** 30 - 1
@@all_cached_fixtures = Hash.new { |h,k| h[k] = {} }
- def self.default_fixture_model_name(fixture_name) # :nodoc:
+ def self.default_fixture_model_name(fixture_set_name) # :nodoc:
ActiveRecord::Base.pluralize_table_names ?
- fixture_name.singularize.camelize :
- fixture_name.camelize
+ fixture_set_name.singularize.camelize :
+ fixture_set_name.camelize
end
- def self.default_fixture_table_name(fixture_name) # :nodoc:
- "#{ActiveRecord::Base.table_name_prefix}"\
- "#{fixture_name.tr('/', '_')}#{ActiveRecord::Base.table_name_suffix}".to_sym
+ def self.default_fixture_table_name(fixture_set_name) # :nodoc:
+ "#{ ActiveRecord::Base.table_name_prefix }"\
+ "#{ fixture_set_name.tr('/', '_') }"\
+ "#{ ActiveRecord::Base.table_name_suffix }".to_sym
end
def self.reset_cache
@@ -411,11 +415,11 @@ module ActiveRecord
cache_for_connection(connection).update(fixtures_map)
end
- def self.instantiate_fixtures(object, fixture_name, fixtures, load_instances = true)
+ def self.instantiate_fixtures(object, fixture_set, load_instances = true)
if load_instances
- fixtures.each do |name, fixture|
+ fixture_set.each do |fixture_name, fixture|
begin
- object.instance_variable_set "@#{name}", fixture.find
+ object.instance_variable_set "@#{fixture_name}", fixture.find
rescue FixtureClassNotFound
nil
end
@@ -424,61 +428,59 @@ module ActiveRecord
end
def self.instantiate_all_loaded_fixtures(object, load_instances = true)
- all_loaded_fixtures.each do |table_name, fixtures|
- ActiveRecord::Fixtures.instantiate_fixtures(object, table_name, fixtures, load_instances)
+ all_loaded_fixtures.each_value do |fixture_set|
+ instantiate_fixtures(object, fixture_set, load_instances)
end
end
cattr_accessor :all_loaded_fixtures
self.all_loaded_fixtures = {}
- def self.create_fixtures(fixtures_directory, table_names, class_names = {})
- table_names = Array(table_names).map(&:to_s)
+ def self.create_fixtures(fixtures_directory, fixture_set_names, class_names = {})
+ fixture_set_names = Array(fixture_set_names).map(&:to_s)
class_names = class_names.stringify_keys
# FIXME: Apparently JK uses this.
connection = block_given? ? yield : ActiveRecord::Base.connection
- files_to_read = table_names.reject { |table_name|
- fixture_is_cached?(connection, table_name)
+ files_to_read = fixture_set_names.reject { |fs_name|
+ fixture_is_cached?(connection, fs_name)
}
unless files_to_read.empty?
connection.disable_referential_integrity do
fixtures_map = {}
- fixture_files = files_to_read.map do |path|
- fixture_name = path
-
- fixtures_map[fixture_name] = new( # ActiveRecord::Fixtures.new
+ fixture_sets = files_to_read.map do |fs_name|
+ fixtures_map[fs_name] = new( # ActiveRecord::Fixtures.new
connection,
- fixture_name,
- class_names[fixture_name.to_s] || default_fixture_model_name(fixture_name),
- ::File.join(fixtures_directory, path))
+ fs_name,
+ class_names[fs_name] || default_fixture_model_name(fs_name),
+ ::File.join(fixtures_directory, fs_name))
end
all_loaded_fixtures.update(fixtures_map)
connection.transaction(:requires_new => true) do
- fixture_files.each do |ff|
- conn = ff.model_class.respond_to?(:connection) ? ff.model_class.connection : connection
- table_rows = ff.table_rows
+ fixture_sets.each do |fs|
+ conn = fs.model_class.respond_to?(:connection) ? fs.model_class.connection : connection
+ table_rows = fs.table_rows
table_rows.keys.each do |table|
conn.delete "DELETE FROM #{conn.quote_table_name(table)}", 'Fixture Delete'
end
- table_rows.each do |table_name,rows|
+ table_rows.each do |fixture_set_name, rows|
rows.each do |row|
- conn.insert_fixture(row, table_name)
+ conn.insert_fixture(row, fixture_set_name)
end
end
end
# Cap primary key sequences to max(pk).
if connection.respond_to?(:reset_pk_sequence!)
- fixture_files.each do |ff|
- connection.reset_pk_sequence!(ff.table_name)
+ fixture_sets.each do |fs|
+ connection.reset_pk_sequence!(fs.table_name)
end
end
end
@@ -486,7 +488,7 @@ module ActiveRecord
cache_fixtures(connection, fixtures_map)
end
end
- cached_fixtures(connection, table_names)
+ cached_fixtures(connection, fixture_set_names)
end
# Returns a consistent, platform-independent identifier for +label+.
@@ -497,26 +499,23 @@ module ActiveRecord
attr_reader :table_name, :name, :fixtures, :model_class
- def initialize(connection, fixture_name, class_name, fixture_path)
- @connection = connection
- @fixture_path = fixture_path
- @name = fixture_name
- @class_name = class_name
-
- @fixtures = {}
+ def initialize(connection, name, class_name, path)
+ @fixtures = {} # Ordered hash
+ @name = name
+ @path = path
- # Should be an AR::Base type class
- if class_name.is_a?(Class)
+ if class_name.is_a?(Class) # TODO: Should be an AR::Base type class, or any?
@model_class = class_name
else
@model_class = class_name.constantize rescue nil
end
- @connection = model_class.connection if model_class && model_class.respond_to?(:connection)
+ @connection = ( model_class.respond_to?(:connection) ?
+ model_class.connection : connection )
@table_name = ( model_class.respond_to?(:table_name) ?
model_class.table_name :
- self.class.default_fixture_table_name(fixture_name) )
+ self.class.default_fixture_table_name(name) )
read_fixture_files
end
@@ -555,8 +554,8 @@ module ActiveRecord
if model_class && model_class < ActiveRecord::Model
# fill in timestamp columns if they aren't specified and the model is set to record_timestamps
if model_class.record_timestamps
- timestamp_column_names.each do |name|
- row[name] = now unless row.key?(name)
+ timestamp_column_names.each do |c_name|
+ row[c_name] = now unless row.key?(c_name)
end
end
@@ -634,26 +633,23 @@ module ActiveRecord
end
def read_fixture_files
- yaml_files = Dir["#{@fixture_path}/**/*.yml"].select { |f|
+ yaml_files = Dir["#{@path}/**/*.yml"].select { |f|
::File.file?(f)
} + [yaml_file_path]
yaml_files.each do |file|
Fixtures::File.open(file) do |fh|
- fh.each do |name, row|
- fixtures[name] = ActiveRecord::Fixture.new(row, model_class)
+ fh.each do |fixture_name, row|
+ fixtures[fixture_name] = ActiveRecord::Fixture.new(row, model_class)
end
end
end
end
def yaml_file_path
- "#{@fixture_path}.yml"
+ "#{@path}.yml"
end
- def yaml_fixtures_key(path)
- ::File.basename(@fixture_path).split(".").first
- end
end
class Fixture #:nodoc:
@@ -708,7 +704,7 @@ module ActiveRecord
class_attribute :fixture_table_names
class_attribute :fixture_class_names
class_attribute :use_transactional_fixtures
- class_attribute :use_instantiated_fixtures # true, false, or :no_instances
+ class_attribute :use_instantiated_fixtures # true, false, or :no_instances
class_attribute :pre_loaded_fixtures
self.fixture_table_names = []
@@ -716,9 +712,8 @@ module ActiveRecord
self.use_instantiated_fixtures = false
self.pre_loaded_fixtures = false
- self.fixture_class_names = Hash.new do |h, fixture_name|
- fixture_name = fixture_name.to_s
- h[fixture_name] = ActiveRecord::Fixtures.default_fixture_model_name(fixture_name)
+ self.fixture_class_names = Hash.new do |h, fixture_set_name|
+ h[fixture_set_name] = ActiveRecord::Fixtures.default_fixture_model_name(fixture_set_name)
end
end
@@ -742,18 +737,18 @@ module ActiveRecord
self.fixture_class_names = self.fixture_class_names.merge(class_names.stringify_keys)
end
- def fixtures(*fixture_names)
- if fixture_names.first == :all
- fixture_names = Dir["#{fixture_path}/**/*.yml"].map { |f|
+ def fixtures(*fixture_set_names)
+ if fixture_set_names.first == :all
+ fixture_set_names = Dir["#{fixture_path}/**/*.yml"].map { |f|
File.basename f, '.yml'
}
else
- fixture_names = fixture_names.flatten.map { |n| n.to_s }
+ fixture_set_names = fixture_set_names.flatten.map { |n| n.to_s }
end
- self.fixture_table_names |= fixture_names
- require_fixture_classes(fixture_names)
- setup_fixture_accessors(fixture_names)
+ self.fixture_table_names |= fixture_set_names
+ require_fixture_classes(fixture_set_names)
+ setup_fixture_accessors(fixture_set_names)
end
def try_to_load_dependency(file_name)
@@ -768,33 +763,39 @@ module ActiveRecord
end
end
- def require_fixture_classes(fixture_names = nil)
- (fixture_names || fixture_table_names).each do |fixture_name|
- file_name = fixture_name.to_s
+ def require_fixture_classes(fixture_set_names = nil)
+ if fixture_set_names
+ fixture_set_names = fixture_set_names.map { |n| n.to_s }
+ else
+ fixture_set_names = fixture_table_names
+ end
+
+ fixture_set_names.each do |file_name|
file_name = file_name.singularize if ActiveRecord::Base.pluralize_table_names
try_to_load_dependency(file_name)
end
end
- def setup_fixture_accessors(fixture_names = nil)
- fixture_names = Array(fixture_names || fixture_table_names)
+ def setup_fixture_accessors(fixture_set_names = nil)
+ fixture_set_names = Array(fixture_set_names || fixture_table_names)
methods = Module.new do
- fixture_names.each do |fixture_name|
- fixture_name = fixture_name.to_s
- accessor_name = fixture_name.tr('/', '_').to_sym
+ fixture_set_names.each do |fs_name|
+ fs_name = fs_name.to_s
+ accessor_name = fs_name.tr('/', '_').to_sym
- define_method(accessor_name) do |*fixtures|
- force_reload = fixtures.pop if fixtures.last == true || fixtures.last == :reload
+ define_method(accessor_name) do |*fixture_names|
+ force_reload = fixture_names.pop if fixture_names.last == true || fixture_names.last == :reload
- @fixture_cache[fixture_name] ||= {}
+ @fixture_cache[fs_name] ||= {}
- instances = fixtures.map do |fixture|
- @fixture_cache[fixture_name].delete(fixture) if force_reload
+ instances = fixture_names.map do |f_name|
+ f_name = f_name.to_s
+ @fixture_cache[fs_name].delete(f_name) if force_reload
- if @loaded_fixtures[fixture_name][fixture.to_s]
- @fixture_cache[fixture_name][fixture] ||= @loaded_fixtures[fixture_name][fixture.to_s].find
+ if @loaded_fixtures[fs_name][f_name]
+ @fixture_cache[fs_name][f_name] ||= @loaded_fixtures[fs_name][f_name].find
else
- raise StandardError, "No entry named '#{fixture}' found for fixture collection '#{fixture_name}'"
+ raise StandardError, "No fixture named '#{f_name}' found for fixture set '#{fs_name}'"
end
end
@@ -901,8 +902,8 @@ module ActiveRecord
ActiveRecord::Fixtures.instantiate_all_loaded_fixtures(self, load_instances?)
else
raise RuntimeError, 'Load fixtures before instantiating them.' if @loaded_fixtures.nil?
- @loaded_fixtures.each do |fixture_name, fixtures|
- ActiveRecord::Fixtures.instantiate_fixtures(self, fixture_name, fixtures, load_instances?)
+ @loaded_fixtures.each_value do |fixture_set|
+ ActiveRecord::Fixtures.instantiate_fixtures(self, fixture_set, load_instances?)
end
end
end
diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb
index 32a1dae6bc..95a2ddcc11 100644
--- a/activerecord/lib/active_record/nested_attributes.rb
+++ b/activerecord/lib/active_record/nested_attributes.rb
@@ -19,10 +19,10 @@ module ActiveRecord
# = Active Record Nested Attributes
#
# Nested attributes allow you to save attributes on associated records
- # through the parent. By default nested attribute updating is turned off,
- # you can enable it using the accepts_nested_attributes_for class method.
- # When you enable nested attributes an attribute writer is defined on
- # the model.
+ # through the parent. By default nested attribute updating is turned off
+ # and you can enable it using the accepts_nested_attributes_for class
+ # method. When you enable nested attributes an attribute writer is
+ # defined on the model.
#
# The attribute writer is named after the association, which means that
# in the following example, two new methods are added to your model:
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index a78e3d08e4..4fedd33d64 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -51,13 +51,13 @@ module ActiveRecord
# Post.find_by "published_at < ?", 2.weeks.ago
#
def find_by(*args)
- where(*args).first
+ where(*args).take
end
# Like <tt>find_by</tt>, except that if no record is found, raises
# an <tt>ActiveRecord::RecordNotFound</tt> error.
def find_by!(*args)
- where(*args).first!
+ where(*args).take!
end
# Gives a record (or N records if a parameter is supplied) without any implied
@@ -170,9 +170,8 @@ module ActiveRecord
# Person.exists?(['name LIKE ?', "%#{query}%"])
# Person.exists?
def exists?(id = false)
- return false if id.nil?
-
id = id.id if ActiveRecord::Model === id
+ return false if id.nil?
join_dependency = construct_join_dependency_for_association_find
relation = construct_relation_for_association_find(join_dependency)
@@ -269,7 +268,7 @@ module ActiveRecord
substitute = connection.substitute_at(column, bind_values.length)
relation = where(table[primary_key].eq(substitute))
relation.bind_values += [[column, id]]
- record = relation.first
+ record = relation.take
unless record
conditions = arel.where_sql
@@ -309,7 +308,7 @@ module ActiveRecord
def find_take
if loaded?
- @records.take(1).first
+ @records.first
else
@take ||= limit(1).to_a.first
end
diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb
index b40bf2b3cf..6a0cdd5917 100644
--- a/activerecord/lib/active_record/relation/predicate_builder.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder.rb
@@ -34,9 +34,6 @@ module ActiveRecord
private
def self.build(attribute, value)
case value
- when ActiveRecord::Relation
- value = value.select(value.klass.arel_table[value.klass.primary_key]) if value.select_values.empty?
- attribute.in(value.arel.ast)
when Array, ActiveRecord::Associations::CollectionProxy
values = value.to_a.map {|x| x.is_a?(ActiveRecord::Model) ? x.id : x}
ranges, values = values.partition {|v| v.is_a?(Range)}
@@ -59,6 +56,9 @@ module ActiveRecord
array_predicates = ranges.map { |range| attribute.in(range) }
array_predicates << values_predicate
array_predicates.inject { |composite, predicate| composite.or(predicate) }
+ when ActiveRecord::Relation
+ value = value.select(value.klass.arel_table[value.klass.primary_key]) if value.select_values.empty?
+ attribute.in(value.arel.ast)
when Range
attribute.in(value)
when ActiveRecord::Model
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 855477eaed..29536b16c4 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -40,7 +40,7 @@ module ActiveRecord
alias extensions extending_values
def includes(*args)
- args.empty? ? self : clone.includes!(*args)
+ args.empty? ? self : spawn.includes!(*args)
end
def includes!(*args)
@@ -51,7 +51,7 @@ module ActiveRecord
end
def eager_load(*args)
- args.blank? ? self : clone.eager_load!(*args)
+ args.blank? ? self : spawn.eager_load!(*args)
end
def eager_load!(*args)
@@ -60,7 +60,7 @@ module ActiveRecord
end
def preload(*args)
- args.blank? ? self : clone.preload!(*args)
+ args.blank? ? self : spawn.preload!(*args)
end
def preload!(*args)
@@ -79,7 +79,7 @@ module ActiveRecord
# User.includes(:posts).where("posts.name = 'foo'").references(:posts)
# # => Query now knows the string references posts, so adds a JOIN
def references(*args)
- args.blank? ? self : clone.references!(*args)
+ args.blank? ? self : spawn.references!(*args)
end
def references!(*args)
@@ -120,7 +120,7 @@ module ActiveRecord
if block_given?
to_a.select { |*block_args| value.call(*block_args) }
else
- clone.select!(value)
+ spawn.select!(value)
end
end
@@ -130,7 +130,7 @@ module ActiveRecord
end
def group(*args)
- args.blank? ? self : clone.group!(*args)
+ args.blank? ? self : spawn.group!(*args)
end
def group!(*args)
@@ -139,7 +139,7 @@ module ActiveRecord
end
def order(*args)
- args.blank? ? self : clone.order!(*args)
+ args.blank? ? self : spawn.order!(*args)
end
def order!(*args)
@@ -165,7 +165,7 @@ module ActiveRecord
# generates a query with 'ORDER BY id ASC, name ASC'.
#
def reorder(*args)
- args.blank? ? self : clone.reorder!(*args)
+ args.blank? ? self : spawn.reorder!(*args)
end
def reorder!(*args)
@@ -175,7 +175,7 @@ module ActiveRecord
end
def joins(*args)
- args.compact.blank? ? self : clone.joins!(*args)
+ args.compact.blank? ? self : spawn.joins!(*args)
end
def joins!(*args)
@@ -186,7 +186,7 @@ module ActiveRecord
end
def bind(value)
- clone.bind!(value)
+ spawn.bind!(value)
end
def bind!(value)
@@ -195,7 +195,7 @@ module ActiveRecord
end
def where(opts, *rest)
- opts.blank? ? self : clone.where!(opts, *rest)
+ opts.blank? ? self : spawn.where!(opts, *rest)
end
def where!(opts, *rest)
@@ -206,7 +206,7 @@ module ActiveRecord
end
def having(opts, *rest)
- opts.blank? ? self : clone.having!(opts, *rest)
+ opts.blank? ? self : spawn.having!(opts, *rest)
end
def having!(opts, *rest)
@@ -217,7 +217,7 @@ module ActiveRecord
end
def limit(value)
- clone.limit!(value)
+ spawn.limit!(value)
end
def limit!(value)
@@ -226,7 +226,7 @@ module ActiveRecord
end
def offset(value)
- clone.offset!(value)
+ spawn.offset!(value)
end
def offset!(value)
@@ -235,7 +235,7 @@ module ActiveRecord
end
def lock(locks = true)
- clone.lock!(locks)
+ spawn.lock!(locks)
end
def lock!(locks = true)
@@ -283,7 +283,7 @@ module ActiveRecord
end
def readonly(value = true)
- clone.readonly!(value)
+ spawn.readonly!(value)
end
def readonly!(value = true)
@@ -292,7 +292,7 @@ module ActiveRecord
end
def create_with(value)
- clone.create_with!(value)
+ spawn.create_with!(value)
end
def create_with!(value)
@@ -301,7 +301,7 @@ module ActiveRecord
end
def from(value)
- clone.from!(value)
+ spawn.from!(value)
end
def from!(value)
@@ -320,7 +320,7 @@ module ActiveRecord
# User.select(:name).uniq.uniq(false)
# # => You can also remove the uniqueness
def uniq(value = true)
- clone.uniq!(value)
+ spawn.uniq!(value)
end
def uniq!(value = true)
@@ -366,7 +366,7 @@ module ActiveRecord
# end
def extending(*modules, &block)
if modules.any? || block
- clone.extending!(*modules, &block)
+ spawn.extending!(*modules, &block)
else
self
end
@@ -382,7 +382,7 @@ module ActiveRecord
end
def reverse_order
- clone.reverse_order!
+ spawn.reverse_order!
end
def reverse_order!
diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb
index 41e55dfd0e..80d087a9ea 100644
--- a/activerecord/lib/active_record/relation/spawn_methods.rb
+++ b/activerecord/lib/active_record/relation/spawn_methods.rb
@@ -5,9 +5,14 @@ require 'active_record/relation/merger'
module ActiveRecord
module SpawnMethods
-
+
+ # This is overridden by Associations::CollectionProxy
+ def spawn #:nodoc:
+ clone
+ end
+
# Merges in the conditions from <tt>other</tt>, if <tt>other</tt> is an <tt>ActiveRecord::Relation</tt>.
- # Returns an array representing the union of the resulting records with <tt>other</tt>, if <tt>other</tt> is an array.
+ # Returns an array representing the intersection of the resulting records with <tt>other</tt>, if <tt>other</tt> is an array.
#
# ==== Examples
#
@@ -16,14 +21,14 @@ module ActiveRecord
#
# recent_posts = Post.order('created_at DESC').first(5)
# Post.where(:published => true).merge(recent_posts)
- # # Returns the union of all published posts with the 5 most recently created posts.
+ # # Returns the intersection of all published posts with the 5 most recently created posts.
# # (This is just an example. You'd probably want to do this with a single query!)
#
def merge(other)
if other.is_a?(Array)
to_a & other
elsif other
- clone.merge!(other)
+ spawn.merge!(other)
else
self
end
@@ -42,7 +47,7 @@ module ActiveRecord
# Post.where('id > 10').order('id asc').except(:where) # discards the where condition but keeps the order
#
def except(*skips)
- result = self.class.new(@klass, table, values.except(*skips))
+ result = Relation.new(klass, table, values.except(*skips))
result.default_scoped = default_scoped
result.extend(*extending_values) if extending_values.any?
result
@@ -56,7 +61,7 @@ module ActiveRecord
# Post.order('id asc').only(:where, :order) # uses the specified order
#
def only(*onlies)
- result = self.class.new(@klass, table, values.slice(*onlies))
+ result = Relation.new(klass, table, values.slice(*onlies))
result.default_scoped = default_scoped
result.extend(*extending_values) if extending_values.any?
result
diff --git a/activerecord/lib/active_record/result.rb b/activerecord/lib/active_record/result.rb
index fb4b89b87b..fd276ccf5d 100644
--- a/activerecord/lib/active_record/result.rb
+++ b/activerecord/lib/active_record/result.rb
@@ -28,6 +28,7 @@ module ActiveRecord
alias :map! :map
alias :collect! :map
+ # Returns true if there are no records.
def empty?
rows.empty?
end
diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb
index 743dfc5a38..64e5640791 100644
--- a/activerecord/lib/active_record/transactions.rb
+++ b/activerecord/lib/active_record/transactions.rb
@@ -302,12 +302,8 @@ module ActiveRecord
def remember_transaction_record_state #:nodoc:
@_start_transaction_state ||= {}
@_start_transaction_state[:id] = id if has_attribute?(self.class.primary_key)
- unless @_start_transaction_state.include?(:new_record)
- @_start_transaction_state[:new_record] = @new_record
- end
- unless @_start_transaction_state.include?(:destroyed)
- @_start_transaction_state[:destroyed] = @destroyed
- end
+ @_start_transaction_state[:new_record] = @new_record
+ @_start_transaction_state[:destroyed] = @destroyed
@_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) + 1
end
diff --git a/activerecord/test/cases/adapters/mysql/connection_test.rb b/activerecord/test/cases/adapters/mysql/connection_test.rb
index fa2ba8d592..5e1c52c9ba 100644
--- a/activerecord/test/cases/adapters/mysql/connection_test.rb
+++ b/activerecord/test/cases/adapters/mysql/connection_test.rb
@@ -120,6 +120,19 @@ class MysqlConnectionTest < ActiveRecord::TestCase
end
end
+ def test_mysql_default_in_strict_mode
+ result = @connection.exec_query "SELECT @@SESSION.sql_mode"
+ assert_equal [["STRICT_ALL_TABLES"]], result.rows
+ end
+
+ def test_mysql_strict_mode_disabled
+ run_without_connection do |orig_connection|
+ ActiveRecord::Model.establish_connection(orig_connection.merge({:strict => false}))
+ result = ActiveRecord::Model.connection.exec_query "SELECT @@SESSION.sql_mode"
+ assert_equal [['']], result.rows
+ end
+ end
+
private
def run_without_connection
diff --git a/activerecord/test/cases/adapters/mysql2/connection_test.rb b/activerecord/test/cases/adapters/mysql2/connection_test.rb
index 8e2b9ca9a5..684c7f5929 100644
--- a/activerecord/test/cases/adapters/mysql2/connection_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/connection_test.rb
@@ -29,6 +29,22 @@ class MysqlConnectionTest < ActiveRecord::TestCase
assert @connection.active?
end
+ # TODO: Below is a straight up copy/paste from mysql/connection_test.rb
+ # I'm not sure what the correct way is to share these tests between
+ # adapters in minitest.
+ def test_mysql_default_in_strict_mode
+ result = @connection.exec_query "SELECT @@SESSION.sql_mode"
+ assert_equal [["STRICT_ALL_TABLES"]], result.rows
+ end
+
+ def test_mysql_strict_mode_disabled
+ run_without_connection do |orig_connection|
+ ActiveRecord::Model.establish_connection(orig_connection.merge({:strict => false}))
+ result = ActiveRecord::Model.connection.exec_query "SELECT @@SESSION.sql_mode"
+ assert_equal [['']], result.rows
+ end
+ end
+
private
def run_without_connection
diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index 2e44005847..08467900f9 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -189,7 +189,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
author = assert_queries(3) { Author.scoped(:includes => {:posts_with_comments => :comments}).find(author_id) } # find the author, then find the posts, then find the comments
author.posts_with_comments.each do |post_with_comments|
assert_equal post_with_comments.comments.length, post_with_comments.comments.count
- assert_nil post_with_comments.comments.uniq!
+ assert_nil post_with_comments.comments.to_a.uniq!
end
end
diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
index 22fd80df28..470f0c3fd3 100644
--- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
@@ -123,11 +123,6 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert active_record.developers.include?(david)
end
- def test_triple_equality
- assert !(Array === Developer.find(1).projects)
- assert Developer.find(1).projects === Array
- end
-
def test_adding_single
jamis = Developer.find(2)
jamis.projects.reload # causing the collection to load
@@ -497,7 +492,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_include_uses_array_include_after_loaded
project = projects(:active_record)
- project.developers.class # force load target
+ project.developers.load_target
developer = project.developers.first
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index f74fe42dc2..13d8c68b33 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -326,12 +326,6 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
authors(:david).readonly_comments.each { |c| assert c.readonly? }
end
- def test_triple_equality
- # sometimes tests on Oracle fail if ORDER BY is not provided therefore add always :order with :first
- assert !(Array === Firm.scoped(:order => "id").first.clients)
- assert Firm.scoped(:order => "id").first.clients === Array
- end
-
def test_finding_default_orders
assert_equal "Summit", Firm.scoped(:order => "id").first.clients.first.name
end
@@ -892,12 +886,11 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
client_id = firm.clients_of_firm.first.id
assert_equal 1, firm.clients_of_firm.size
- cleared = firm.clients_of_firm.clear
+ firm.clients_of_firm.clear
assert_equal 0, firm.clients_of_firm.size
assert_equal 0, firm.clients_of_firm(true).size
assert_equal [], Client.destroyed_client_ids[firm.id]
- assert_equal firm.clients_of_firm.object_id, cleared.object_id
# Should not be destroyed since the association is not dependent.
assert_nothing_raised do
@@ -1359,7 +1352,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_include_uses_array_include_after_loaded
firm = companies(:first_firm)
- firm.clients.class # force load target
+ firm.clients.load_target
client = firm.clients.first
@@ -1409,7 +1402,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_calling_first_or_last_on_loaded_association_should_not_fetch_with_query
firm = companies(:first_firm)
- firm.clients.class # force load target
+ firm.clients.load_target
assert firm.clients.loaded?
assert_no_queries do
@@ -1708,4 +1701,18 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
firm = companies(:first_firm)
assert_equal [accounts(:signals37)], firm.accounts.open
end
+
+ test "first_or_initialize adds the record to the association" do
+ firm = Firm.create! name: 'omg'
+ client = firm.clients_of_firm.first_or_initialize
+ assert_equal [client], firm.clients_of_firm
+ end
+
+ test "first_or_create adds the record to the association" do
+ firm = Firm.create! name: 'omg'
+ firm.clients_of_firm.load_target
+ client = firm.clients_of_firm.first_or_create name: 'lol'
+ assert_equal [client], firm.clients_of_firm
+ assert_equal [client], firm.reload.clients_of_firm
+ end
end
diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb
index 3606ce691c..20b0eeb5ea 100644
--- a/activerecord/test/cases/associations/join_model_test.rb
+++ b/activerecord/test/cases/associations/join_model_test.rb
@@ -676,7 +676,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
def test_has_many_through_include_uses_array_include_after_loaded
david = authors(:david)
- david.categories.class # force load target
+ david.categories.load_target
category = david.categories.first
diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb
index c93c91803e..8ef3bfef15 100644
--- a/activerecord/test/cases/autosave_association_test.rb
+++ b/activerecord/test/cases/autosave_association_test.rb
@@ -872,7 +872,7 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
2.times { |i| @pirate.parrots.create!(:name => "parrots_#{i}") }
before = @pirate.parrots.map { |c| c.mark_for_destruction ; c }
- class << @pirate.parrots
+ class << @pirate.association(:parrots)
def destroy(*args)
super
raise 'Oh noes!'
@@ -1277,7 +1277,7 @@ module AutosaveAssociationOnACollectionAssociationTests
def test_should_not_load_the_associated_models_if_they_were_not_loaded_yet
assert_queries(1) { @pirate.catchphrase = 'Arr'; @pirate.save! }
- @pirate.send(@association_name).class # hack to load the target
+ @pirate.send(@association_name).load_target
assert_queries(3) do
@pirate.catchphrase = 'Yarr'
diff --git a/activerecord/test/cases/column_test.rb b/activerecord/test/cases/column_test.rb
index 4fcf8a33a4..4111a5f808 100644
--- a/activerecord/test/cases/column_test.rb
+++ b/activerecord/test/cases/column_test.rb
@@ -48,6 +48,34 @@ module ActiveRecord
column.type_cast(false)
end
end
+
+ def test_type_cast_time
+ column = Column.new("field", nil, "time")
+ assert_equal nil, column.type_cast('')
+ assert_equal nil, column.type_cast(' ')
+
+ time_string = Time.now.utc.strftime("%T")
+ assert_equal time_string, column.type_cast(time_string).strftime("%T")
+ end
+
+ def test_type_cast_datetime_and_timestamp
+ [Column.new("field", nil, "datetime"), Column.new("field", nil, "timestamp")].each do |column|
+ assert_equal nil, column.type_cast('')
+ assert_equal nil, column.type_cast(' ')
+
+ datetime_string = Time.now.utc.strftime("%FT%T")
+ assert_equal datetime_string, column.type_cast(datetime_string).strftime("%FT%T")
+ end
+ end
+
+ def test_type_cast_date
+ column = Column.new("field", nil, "date")
+ assert_equal nil, column.type_cast('')
+ assert_equal nil, column.type_cast(' ')
+
+ date_string = Time.now.utc.strftime("%F")
+ assert_equal date_string, column.type_cast(date_string).strftime("%F")
+ end
end
end
end
diff --git a/activerecord/test/cases/connection_adapters/quoting_test.rb b/activerecord/test/cases/connection_adapters/quoting_test.rb
new file mode 100644
index 0000000000..59dcb96ebc
--- /dev/null
+++ b/activerecord/test/cases/connection_adapters/quoting_test.rb
@@ -0,0 +1,13 @@
+require "cases/helper"
+
+module ActiveRecord
+ module ConnectionAdapters
+ module Quoting
+ class QuotingTest < ActiveRecord::TestCase
+ def test_quoting_classes
+ assert_equal "'Object'", AbstractAdapter.new(nil).quote(Object)
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index 54801bd101..c960773284 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -32,6 +32,7 @@ class FinderTest < ActiveRecord::TestCase
assert Topic.exists?(:author_name => "Mary", :approved => true)
assert Topic.exists?(["parent_id = ?", 1])
assert !Topic.exists?(45)
+ assert !Topic.exists?(Topic.new)
begin
assert !Topic.exists?("foo")
@@ -113,6 +114,10 @@ class FinderTest < ActiveRecord::TestCase
assert_equal [], Topic.find([])
end
+ def test_find_doesnt_have_implicit_ordering
+ assert_sql(/^((?!ORDER).)*$/) { Topic.find(1) }
+ end
+
def test_find_by_ids_missing_one
assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, 2, 45) }
end
diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb
index 345ae0b582..20279f814b 100644
--- a/activerecord/test/cases/helper.rb
+++ b/activerecord/test/cases/helper.rb
@@ -80,8 +80,8 @@ class ActiveSupport::TestCase
self.use_instantiated_fixtures = false
self.use_transactional_fixtures = true
- def create_fixtures(*table_names, &block)
- ActiveRecord::Fixtures.create_fixtures(ActiveSupport::TestCase.fixture_path, table_names, fixture_class_names, &block)
+ def create_fixtures(*fixture_set_names, &block)
+ ActiveRecord::Fixtures.create_fixtures(ActiveSupport::TestCase.fixture_path, fixture_set_names, fixture_class_names, &block)
end
end
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index 3ef357e297..76b868c7b4 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -255,7 +255,7 @@ class RelationTest < ActiveRecord::TestCase
assert_equal nil, Developer.none.calculate(:average, 'salary')
end
end
-
+
def test_null_relation_metadata_methods
assert_equal "", Developer.none.to_sql
assert_equal({}, Developer.none.where_values_hash)
@@ -644,6 +644,7 @@ class RelationTest < ActiveRecord::TestCase
assert ! davids.exists?(authors(:mary).id)
assert ! davids.exists?("42")
assert ! davids.exists?(42)
+ assert ! davids.exists?(davids.new)
fake = Author.where(:name => 'fake author')
assert ! fake.exists?
@@ -1287,6 +1288,10 @@ class RelationTest < ActiveRecord::TestCase
assert_equal nil, Post.scoped.find_by("1 = 0")
end
+ test "find_by doesn't have implicit ordering" do
+ assert_sql(/^((?!ORDER).)*$/) { Post.find_by(author_id: 2) }
+ end
+
test "find_by! with hash conditions returns the first matching record" do
assert_equal posts(:eager_other), Post.order(:id).find_by!(author_id: 2)
end
@@ -1299,6 +1304,10 @@ class RelationTest < ActiveRecord::TestCase
assert_equal posts(:eager_other), Post.order(:id).find_by!('author_id = ?', 2)
end
+ test "find_by! doesn't have implicit ordering" do
+ assert_sql(/^((?!ORDER).)*$/) { Post.find_by!(author_id: 2) }
+ end
+
test "find_by! raises RecordNotFound if the record is missing" do
assert_raises(ActiveRecord::RecordNotFound) do
Post.scoped.find_by!("1 = 0")
diff --git a/activerecord/test/cases/transaction_callbacks_test.rb b/activerecord/test/cases/transaction_callbacks_test.rb
index f8b3e01a49..9846f5b12d 100644
--- a/activerecord/test/cases/transaction_callbacks_test.rb
+++ b/activerecord/test/cases/transaction_callbacks_test.rb
@@ -290,3 +290,34 @@ class TransactionObserverCallbacksTest < ActiveRecord::TestCase
assert_equal %w{ after_rollback }, topic.history
end
end
+
+class SaveFromAfterCommitBlockTest < ActiveRecord::TestCase
+ self.use_transactional_fixtures = false
+
+ class TopicWithSaveInCallback < ActiveRecord::Base
+ self.table_name = :topics
+ after_commit :cache_topic, :on => :create
+ after_commit :call_update, :on => :update
+ attr_accessor :cached, :record_updated
+
+ def call_update
+ self.record_updated = true
+ end
+
+ def cache_topic
+ unless cached
+ self.cached = true
+ self.save
+ else
+ self.cached = false
+ end
+ end
+ end
+
+ def test_after_commit_in_save
+ topic = TopicWithSaveInCallback.new()
+ topic.save
+ assert_equal true, topic.cached
+ assert_equal true, topic.record_updated
+ end
+end
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 7082d5dc86..cef08cd99c 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -38,7 +38,9 @@ ActiveRecord::Schema.define do
create_table :admin_users, :force => true do |t|
t.string :name
t.text :settings, :null => true
- t.text :preferences, :null => false, :default => ""
+ # MySQL does not allow default values for blobs. Fake it out with a
+ # big varchar below.
+ t.string :preferences, :null => false, :default => '', :limit => 1024
t.references :account
end
diff --git a/activerecord/test/schema/sqlite_specific_schema.rb b/activerecord/test/schema/sqlite_specific_schema.rb
index ea05b35fe0..e9ddeb32cf 100644
--- a/activerecord/test/schema/sqlite_specific_schema.rb
+++ b/activerecord/test/schema/sqlite_specific_schema.rb
@@ -1,5 +1,5 @@
ActiveRecord::Schema.define do
- # For sqlite 3.1.0+, make a table with a autoincrement column
+ # For sqlite 3.1.0+, make a table with an autoincrement column
if supports_autoincrement?
create_table :table_with_autoincrement, :force => true do |t|
t.column :name, :string
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index 82921741b8..636aca2b8f 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,39 +1,56 @@
## Rails 4.0.0 (unreleased) ##
-* Inflector no longer applies ice -> ouse to words like slice, police, ets *Wes Morgan*
+* `Object#try` can't call private methods. *Vasiliy Ermolovich*
-* Add `ActiveSupport::Deprecations.behavior = :silence` to completely ignore Rails runtime deprecations *twinturbo*
+* `AS::Callbacks#run_callbacks` remove `key` argument. *Francesco Rodriguez*
-* Make Module#delegate stop using `send` - can no longer delegate to private methods. *dasch*
+* `deep_dup` works more expectedly now and duplicates also values in +Hash+ instances and elements in +Array+ instances. *Alexey Gaziev*
-* AS::Callbacks: deprecate `:rescuable` option. *Bogdan Gusiev*
+* Inflector no longer applies ice -> ouse to words like slice, police, ets *Wes Morgan*
-* Adds Integer#ordinal to get the ordinal suffix string of an integer. *Tim Gildea*
+* Add `ActiveSupport::Deprecations.behavior = :silence` to completely ignore Rails runtime deprecations *twinturbo*
-* AS::Callbacks: `:per_key` option is no longer supported
+* Make Module#delegate stop using `send` - can no longer delegate to private methods. *dasch*
-* `AS::Callbacks#define_callbacks`: add `:skip_after_callbacks_if_terminated` option.
+* AS::Callbacks: deprecate `:rescuable` option. *Bogdan Gusiev*
-* Add html_escape_once to ERB::Util, and delegate escape_once tag helper to it. *Carlos Antonio da Silva*
+* Adds Integer#ordinal to get the ordinal suffix string of an integer. *Tim Gildea*
-* Remove ActiveSupport::TestCase#pending method, use `skip` instead. *Carlos Antonio da Silva*
+* AS::Callbacks: `:per_key` option is no longer supported
-* Deprecates the compatibility method Module#local_constant_names,
- use Module#local_constants instead (which returns symbols). *fxn*
+* `AS::Callbacks#define_callbacks`: add `:skip_after_callbacks_if_terminated` option.
-* Deletes the compatibility method Module#method_names,
- use Module#methods from now on (which returns symbols). *fxn*
+* Add html_escape_once to ERB::Util, and delegate escape_once tag helper to it. *Carlos Antonio da Silva*
-* Deletes the compatibility method Module#instance_method_names,
- use Module#instance_methods from now on (which returns symbols). *fxn*
+* Remove ActiveSupport::TestCase#pending method, use `skip` instead. *Carlos Antonio da Silva*
-* BufferedLogger is deprecated. Use ActiveSupport::Logger, or the logger
- from Ruby stdlib.
+* Deprecates the compatibility method Module#local_constant_names,
+ use Module#local_constants instead (which returns symbols). *fxn*
-* Unicode database updated to 6.1.0.
+* Deletes the compatibility method Module#method_names,
+ use Module#methods from now on (which returns symbols). *fxn*
-* Adds `encode_big_decimal_as_string` option to force JSON serialization of BigDecimals as numeric instead
- of wrapping them in strings for safety.
+* Deletes the compatibility method Module#instance_method_names,
+ use Module#instance_methods from now on (which returns symbols). *fxn*
+
+* BufferedLogger is deprecated. Use ActiveSupport::Logger, or the logger
+ from Ruby stdlib.
+
+* Unicode database updated to 6.1.0.
+
+* Adds `encode_big_decimal_as_string` option to force JSON serialization of BigDecimals as numeric instead
+ of wrapping them in strings for safety.
+
+
+## Rails 3.2.4 (unreleased) ##
+
+* Added #beginning_of_hour and #end_of_hour to Time and DateTime core
+ extensions. *Mark J. Titorenko*
+
+
+## Rails 3.2.3 (March 30, 2012) ##
+
+* No changes.
## Rails 3.2.2 (March 1, 2012) ##
@@ -228,7 +245,7 @@
* Hash.from_xml no longer loses attributes on tags containing only whitespace *André Arko*
-## Rails 3.0.6 (April 5, 2011) ##
+## Rails 3.0.6 (April 5, 2011) ##
* No changes.
diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb
index dbf0c25c5c..8f018dcbc6 100644
--- a/activesupport/lib/active_support.rb
+++ b/activesupport/lib/active_support.rb
@@ -22,20 +22,6 @@
#++
require 'securerandom'
-
-module ActiveSupport
- class << self
- attr_accessor :load_all_hooks
- def on_load_all(&hook) load_all_hooks << hook end
- def load_all!; load_all_hooks.each { |hook| hook.call } end
- end
- self.load_all_hooks = []
-
- on_load_all do
- [Dependencies, Deprecation, Gzip, MessageVerifier, Multibyte]
- end
-end
-
require "active_support/dependencies/autoload"
require "active_support/version"
require "active_support/logger"
@@ -44,6 +30,7 @@ module ActiveSupport
extend ActiveSupport::Autoload
autoload :Concern
+ autoload :Dependencies
autoload :DescendantsTracker
autoload :FileUpdateChecker
autoload :LogSubscriber
diff --git a/activesupport/lib/active_support/cache/file_store.rb b/activesupport/lib/active_support/cache/file_store.rb
index e7316b23b3..89bdb741d0 100644
--- a/activesupport/lib/active_support/cache/file_store.rb
+++ b/activesupport/lib/active_support/cache/file_store.rb
@@ -1,7 +1,7 @@
require 'active_support/core_ext/file/atomic'
require 'active_support/core_ext/string/conversions'
require 'active_support/core_ext/object/inclusion'
-require 'rack/utils'
+require 'uri/common'
module ActiveSupport
module Cache
@@ -126,7 +126,7 @@ module ActiveSupport
# Translate a key into a file path.
def key_file_path(key)
- fname = Rack::Utils.escape(key)
+ fname = URI.encode_www_form_component(key)
hash = Zlib.adler32(fname)
hash, dir_1 = hash.divmod(0x1000)
dir_2 = hash.modulo(0x1000)
@@ -144,7 +144,7 @@ module ActiveSupport
# Translate a file path into a key.
def file_path_key(path)
fname = path[cache_path.to_s.size..-1].split(File::SEPARATOR, 4).last
- Rack::Utils.unescape(fname)
+ URI.decode_www_form_component(fname, Encoding::UTF_8)
end
# Delete empty directories in the cache.
diff --git a/activesupport/lib/active_support/cache/memory_store.rb b/activesupport/lib/active_support/cache/memory_store.rb
index b15bb42c88..7fd5e3b53d 100644
--- a/activesupport/lib/active_support/cache/memory_store.rb
+++ b/activesupport/lib/active_support/cache/memory_store.rb
@@ -137,6 +137,7 @@ module ActiveSupport
def write_entry(key, entry, options) # :nodoc:
synchronize do
old_entry = @data[key]
+ return false if @data.key?(key) && options[:unless_exist]
@cache_size -= old_entry.size if old_entry
@cache_size += entry.size
@key_access[key] = Time.now.to_f
diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb
index cbeba3139a..c6c7e2821b 100644
--- a/activesupport/lib/active_support/callbacks.rb
+++ b/activesupport/lib/active_support/callbacks.rb
@@ -54,7 +54,6 @@ module ActiveSupport
# saving...
# - save
# saved
- #
module Callbacks
extend Concern
@@ -73,9 +72,7 @@ module ActiveSupport
# run_callbacks :save do
# save
# end
- #
- def run_callbacks(kind, key = nil, &block)
- #TODO: deprecate key argument
+ def run_callbacks(kind, &block)
runner_name = self.class.__define_callbacks(kind, self)
send(runner_name, &block)
end
@@ -199,7 +196,6 @@ module ActiveSupport
# yield self
# end
# end
- #
def define_conditional_callback
name = "_conditional_callback_#{@kind}_#{next_id}"
@klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
@@ -253,7 +249,6 @@ module ActiveSupport
# Objects::
# a method is created that calls the before_foo method
# on the object.
- #
def _compile_filter(filter)
method_name = "_callback_#{@kind}_#{next_id}"
case filter
@@ -405,7 +400,6 @@ module ActiveSupport
# will be called only when it returns a false value.
# * <tt>:prepend</tt> - If true, the callback will be prepended to the existing
# chain rather than appended.
- #
def set_callback(name, *filter_list, &block)
mapped = nil
@@ -430,7 +424,6 @@ module ActiveSupport
# class Writer < Person
# skip_callback :validate, :before, :check_membership, :if => lambda { self.age > 18 }
# end
- #
def skip_callback(name, *filter_list, &block)
__update_callbacks(name, filter_list, block) do |target, chain, type, filters, options|
filters.each do |filter|
@@ -449,7 +442,6 @@ module ActiveSupport
end
# Remove all set callbacks for the given event.
- #
def reset_callbacks(symbol)
callbacks = send("_#{symbol}_callbacks")
@@ -530,7 +522,6 @@ module ActiveSupport
# define_callbacks :save, :scope => [:name]
#
# would call <tt>Audit#save</tt>.
- #
def define_callbacks(*callbacks)
config = callbacks.last.is_a?(Hash) ? callbacks.pop : {}
callbacks.each do |callback|
diff --git a/activesupport/lib/active_support/core_ext/array/wrap.rb b/activesupport/lib/active_support/core_ext/array/wrap.rb
index 4834eca8b1..9ea93d7226 100644
--- a/activesupport/lib/active_support/core_ext/array/wrap.rb
+++ b/activesupport/lib/active_support/core_ext/array/wrap.rb
@@ -25,9 +25,6 @@ class Array
# Array(:foo => :bar) # => [[:foo, :bar]]
# Array.wrap(:foo => :bar) # => [{:foo => :bar}]
#
- # Array("foo\nbar") # => ["foo\n", "bar"], in Ruby 1.8
- # Array.wrap("foo\nbar") # => ["foo\nbar"]
- #
# There's also a related idiom that uses the splat operator:
#
# [*object]
diff --git a/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb b/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb
index 5cb528cfe9..fa1dbfdf06 100644
--- a/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb
+++ b/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb
@@ -2,30 +2,33 @@ require 'active_support/core_ext/array/extract_options'
# Extends the class object with class and instance accessors for class attributes,
# just like the native attr* accessors for instance attributes.
-#
-# Note that unlike +class_attribute+, if a subclass changes the value then that would
-# also change the value for parent class. Similarly if parent class changes the value
-# then that would change the value of subclasses too.
-#
-# class Person
-# cattr_accessor :hair_colors
-# end
-#
-# Person.hair_colors = [:brown, :black, :blonde, :red]
-# Person.hair_colors # => [:brown, :black, :blonde, :red]
-# Person.new.hair_colors # => [:brown, :black, :blonde, :red]
-#
-# To opt out of the instance writer method, pass :instance_writer => false.
-# To opt out of the instance reader method, pass :instance_reader => false.
-# To opt out of both instance methods, pass :instance_accessor => false.
-#
-# class Person
-# cattr_accessor :hair_colors, :instance_writer => false, :instance_reader => false
-# end
-#
-# Person.new.hair_colors = [:brown] # => NoMethodError
-# Person.new.hair_colors # => NoMethodError
class Class
+ # Defines a class attribute if it's not defined and creates a reader method that
+ # returns the attribute value.
+ #
+ # class Person
+ # cattr_reader :hair_colors
+ # end
+ #
+ # Person.class_variable_set("@@hair_colors", [:brown, :black])
+ # Person.hair_colors # => [:brown, :black]
+ # Person.new.hair_colors # => [:brown, :black]
+ #
+ # The attribute name must be a valid method name in Ruby.
+ #
+ # class Person
+ # cattr_reader :"1_Badname "
+ # end
+ # # => NameError: invalid attribute name
+ #
+ # If you want to opt out the instance reader method, you can pass <tt>instance_reader: false</tt>
+ # or <tt>instance_accessor: false</tt>.
+ #
+ # class Person
+ # cattr_reader :hair_colors, instance_reader: false
+ # end
+ #
+ # Person.new.hair_colors # => NoMethodError
def cattr_reader(*syms)
options = syms.extract_options!
syms.each do |sym|
@@ -50,6 +53,43 @@ class Class
end
end
+ # Defines a class attribute if it's not defined and creates a writer method to allow
+ # assignment to the attribute.
+ #
+ # class Person
+ # cattr_writer :hair_colors
+ # end
+ #
+ # Person.hair_colors = [:brown, :black]
+ # Person.class_variable_get("@@hair_colors") # => [:brown, :black]
+ # Person.new.hair_colors = [:blonde, :red]
+ # Person.class_variable_get("@@hair_colors") # => [:blonde, :red]
+ #
+ # The attribute name must be a valid method name in Ruby.
+ #
+ # class Person
+ # cattr_writer :"1_Badname "
+ # end
+ # # => NameError: invalid attribute name
+ #
+ # If you want to opt out the instance writer method, pass <tt>instance_writer: false</tt>
+ # or <tt>instance_accessor: false</tt>.
+ #
+ # class Person
+ # cattr_writer :hair_colors, instance_writer: false
+ # end
+ #
+ # Person.new.hair_colors = [:blonde, :red] # => NoMethodError
+ #
+ # Also, you can pass a block to set up the attribute with a default value.
+ #
+ # class Person
+ # cattr_writer :hair_colors do
+ # [:brown, :black, :blonde, :red]
+ # end
+ # end
+ #
+ # Person.class_variable_get("@@hair_colors") # => [:brown, :black, :blonde, :red]
def cattr_writer(*syms)
options = syms.extract_options!
syms.each do |sym|
@@ -75,6 +115,54 @@ class Class
end
end
+ # Defines both class and instance accessors for class attributes.
+ #
+ # class Person
+ # cattr_accessor :hair_colors
+ # end
+ #
+ # Person.hair_colors = [:brown, :black, :blonde, :red]
+ # Person.hair_colors # => [:brown, :black, :blonde, :red]
+ # Person.new.hair_colors # => [:brown, :black, :blonde, :red]
+ #
+ # If a subclass changes the value then that would also change the value for
+ # parent class. Similarly if parent class changes the value then that would
+ # change the value of subclasses too.
+ #
+ # class Male < Person
+ # end
+ #
+ # Male.hair_colors << :blue
+ # Person.hair_colors # => [:brown, :black, :blonde, :red, :blue]
+ #
+ # To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
+ # To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
+ #
+ # class Person
+ # cattr_accessor :hair_colors, instance_writer: false, instance_reader: false
+ # end
+ #
+ # Person.new.hair_colors = [:brown] # => NoMethodError
+ # Person.new.hair_colors # => NoMethodError
+ #
+ # Or pass <tt>instance_accessor: false</tt>, to opt out both instance methods.
+ #
+ # class Person
+ # cattr_accessor :hair_colors, instance_accessor: false
+ # end
+ #
+ # Person.new.hair_colors = [:brown] # => NoMethodError
+ # Person.new.hair_colors # => NoMethodError
+ #
+ # Also you can pass a block to set up the attribute with a default value.
+ #
+ # class Person
+ # cattr_accessor :hair_colors do
+ # [:brown, :black, :blonde, :red]
+ # end
+ # end
+ #
+ # Person.class_variable_get("@@hair_colors") #=> [:brown, :black, :blonde, :red]
def cattr_accessor(*syms, &blk)
cattr_reader(*syms)
cattr_writer(*syms, &blk)
diff --git a/activesupport/lib/active_support/core_ext/hash.rb b/activesupport/lib/active_support/core_ext/hash.rb
index fd1cda991e..501483498d 100644
--- a/activesupport/lib/active_support/core_ext/hash.rb
+++ b/activesupport/lib/active_support/core_ext/hash.rb
@@ -1,6 +1,5 @@
require 'active_support/core_ext/hash/conversions'
require 'active_support/core_ext/hash/deep_merge'
-require 'active_support/core_ext/hash/deep_dup'
require 'active_support/core_ext/hash/diff'
require 'active_support/core_ext/hash/except'
require 'active_support/core_ext/hash/indifferent_access'
diff --git a/activesupport/lib/active_support/core_ext/hash/deep_dup.rb b/activesupport/lib/active_support/core_ext/hash/deep_dup.rb
deleted file mode 100644
index 9ab179c566..0000000000
--- a/activesupport/lib/active_support/core_ext/hash/deep_dup.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-class Hash
- # Returns a deep copy of hash.
- def deep_dup
- duplicate = self.dup
- duplicate.each_pair do |k,v|
- duplicate[k] = v.is_a?(Hash) ? v.deep_dup : v
- end
- duplicate
- end
-end
diff --git a/activesupport/lib/active_support/core_ext/integer/multiple.rb b/activesupport/lib/active_support/core_ext/integer/multiple.rb
index 8dff217ddc..7c6c2f1ca7 100644
--- a/activesupport/lib/active_support/core_ext/integer/multiple.rb
+++ b/activesupport/lib/active_support/core_ext/integer/multiple.rb
@@ -1,5 +1,9 @@
class Integer
# Check whether the integer is evenly divisible by the argument.
+ #
+ # 0.multiple_of?(0) #=> true
+ # 6.multiple_of?(5) #=> false
+ # 10.multiple_of?(2) #=> true
def multiple_of?(number)
number != 0 ? self % number == 0 : zero?
end
diff --git a/activesupport/lib/active_support/core_ext/numeric/time.rb b/activesupport/lib/active_support/core_ext/numeric/time.rb
index 822f766af7..2bf3d1f278 100644
--- a/activesupport/lib/active_support/core_ext/numeric/time.rb
+++ b/activesupport/lib/active_support/core_ext/numeric/time.rb
@@ -8,13 +8,13 @@ class Numeric
# These methods use Time#advance for precise date calculations when using from_now, ago, etc.
# as well as adding or subtracting their results from a Time object. For example:
#
- # # equivalent to Time.now.advance(:months => 1)
+ # # equivalent to Time.current.advance(:months => 1)
# 1.month.from_now
#
- # # equivalent to Time.now.advance(:years => 2)
+ # # equivalent to Time.current.advance(:years => 2)
# 2.years.from_now
#
- # # equivalent to Time.now.advance(:months => 4, :years => 5)
+ # # equivalent to Time.current.advance(:months => 4, :years => 5)
# (4.months + 5.years).from_now
#
# While these methods provide precise calculation when used as in the examples above, care
diff --git a/activesupport/lib/active_support/core_ext/object.rb b/activesupport/lib/active_support/core_ext/object.rb
index 9ad1e12699..ec2157221f 100644
--- a/activesupport/lib/active_support/core_ext/object.rb
+++ b/activesupport/lib/active_support/core_ext/object.rb
@@ -1,6 +1,7 @@
require 'active_support/core_ext/object/acts_like'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/object/duplicable'
+require 'active_support/core_ext/object/deep_dup'
require 'active_support/core_ext/object/try'
require 'active_support/core_ext/object/inclusion'
diff --git a/activesupport/lib/active_support/core_ext/object/deep_dup.rb b/activesupport/lib/active_support/core_ext/object/deep_dup.rb
new file mode 100644
index 0000000000..2c4383ac94
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/object/deep_dup.rb
@@ -0,0 +1,22 @@
+class Object
+ # Returns a deep copy of object if it's duplicable.
+ def deep_dup
+ duplicable? ? dup : self
+ end
+end
+
+class Array
+ # Returns a deep copy of array.
+ def deep_dup
+ map { |it| it.deep_dup }
+ end
+end
+
+class Hash
+ # Returns a deep copy of hash.
+ def deep_dup
+ each_with_object(dup) do |(key, value), hash|
+ hash[key.deep_dup] = value.deep_dup
+ end
+ end
+end
diff --git a/activesupport/lib/active_support/core_ext/object/try.rb b/activesupport/lib/active_support/core_ext/object/try.rb
index e77a9da0ec..40a8101ca3 100644
--- a/activesupport/lib/active_support/core_ext/object/try.rb
+++ b/activesupport/lib/active_support/core_ext/object/try.rb
@@ -1,5 +1,5 @@
class Object
- # Invokes the method identified by the symbol +method+, passing it any arguments
+ # Invokes the public method identified by the symbol +method+, passing it any arguments
# and/or the block specified, just like the regular Ruby <tt>Object#send</tt> does.
#
# *Unlike* that method however, a +NoMethodError+ exception will *not* be raised
@@ -29,7 +29,7 @@ class Object
if a.empty? && block_given?
yield self
else
- __send__(*a, &b)
+ public_send(*a, &b)
end
end
end
diff --git a/activesupport/lib/active_support/core_ext/string/access.rb b/activesupport/lib/active_support/core_ext/string/access.rb
index 23aaee9c43..5c32a2453d 100644
--- a/activesupport/lib/active_support/core_ext/string/access.rb
+++ b/activesupport/lib/active_support/core_ext/string/access.rb
@@ -1,18 +1,79 @@
require 'active_support/multibyte'
class String
+ # If you pass a single Fixnum, returns a substring of one character at that
+ # position. The first character of the string is at position 0, the next at
+ # position 1, and so on. If a range is supplied, a substring containing
+ # characters at offsets given by the range is returned. In both cases, if an
+ # offset is negative, it is counted from the end of the string. Returns nil
+ # if the initial offset falls outside the string. Returns an empty string if
+ # the beginning of the range is greater than the end of the string.
+ #
+ # str = "hello"
+ # str.at(0) #=> "h"
+ # str.at(1..3) #=> "ell"
+ # str.at(-2) #=> "l"
+ # str.at(-2..-1) #=> "lo"
+ # str.at(5) #=> nil
+ # str.at(5..-1) #=> ""
+ #
+ # If a Regexp is given, the matching portion of the string is returned.
+ # If a String is given, that given string is returned if it occurs in
+ # the string. In both cases, nil is returned if there is no match.
+ #
+ # str = "hello"
+ # str.at(/lo/) #=> "lo"
+ # str.at(/ol/) #=> nil
+ # str.at("lo") #=> "lo"
+ # str.at("ol") #=> nil
def at(position)
self[position]
end
+ # Returns a substring from the given position to the end of the string.
+ # If the position is negative, it is counted from the end of the string.
+ #
+ # str = "hello"
+ # str.from(0) #=> "hello"
+ # str.from(3) #=> "lo"
+ # str.from(-2) #=> "lo"
+ #
+ # You can mix it with +to+ method and do fun things like:
+ #
+ # str = "hello"
+ # str.from(0).to(-1) #=> "hello"
+ # str.from(1).to(-2) #=> "ell"
def from(position)
self[position..-1]
end
+ # Returns a substring from the beginning of the string to the given position.
+ # If the position is negative, it is counted from the end of the string.
+ #
+ # str = "hello"
+ # str.to(0) #=> "h"
+ # str.to(3) #=> "hell"
+ # str.to(-2) #=> "hell"
+ #
+ # You can mix it with +from+ method and do fun things like:
+ #
+ # str = "hello"
+ # str.from(0).to(-1) #=> "hello"
+ # str.from(1).to(-2) #=> "ell"
def to(position)
self[0..position]
end
+ # Returns the first character. If a limit is supplied, returns a substring
+ # from the beginning of the string until it reaches the limit value. If the
+ # given limit is greater than or equal to the string length, returns self.
+ #
+ # str = "hello"
+ # str.first #=> "h"
+ # str.first(1) #=> "h"
+ # str.first(2) #=> "he"
+ # str.first(0) #=> ""
+ # str.first(6) #=> "hello"
def first(limit = 1)
if limit == 0
''
@@ -23,6 +84,16 @@ class String
end
end
+ # Returns the last character of the string. If a limit is supplied, returns a substring
+ # from the end of the string until it reaches the limit value (counting backwards). If
+ # the given limit is greater than or equal to the string length, returns self.
+ #
+ # str = "hello"
+ # str.last #=> "o"
+ # str.last(1) #=> "o"
+ # str.last(2) #=> "lo"
+ # str.last(0) #=> ""
+ # str.last(6) #=> "hello"
def last(limit = 1)
if limit == 0
''
diff --git a/activesupport/lib/active_support/core_ext/string/conversions.rb b/activesupport/lib/active_support/core_ext/string/conversions.rb
index 9084bbee32..022b376aec 100644
--- a/activesupport/lib/active_support/core_ext/string/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/string/conversions.rb
@@ -15,6 +15,12 @@ class String
end
end
+ # Converts a string to a Date value.
+ #
+ # "1-1-2012".to_date #=> Sun, 01 Jan 2012
+ # "01/01/2012".to_date #=> Sun, 01 Jan 2012
+ # "2012-12-13".to_date #=> Thu, 13 Dec 2012
+ # "12/13/2012".to_date #=> ArgumentError: invalid date
def to_date
unless blank?
date_values = ::Date._parse(self, false).values_at(:year, :mon, :mday)
@@ -23,6 +29,12 @@ class String
end
end
+ # Converts a string to a DateTime value.
+ #
+ # "1-1-2012".to_datetime #=> Sun, 01 Jan 2012 00:00:00 +0000
+ # "01/01/2012 23:59:59".to_datetime #=> Sun, 01 Jan 2012 23:59:59 +0000
+ # "2012-12-13 12:50".to_datetime #=> Thu, 13 Dec 2012 12:50:00 +0000
+ # "12/13/2012".to_datetime #=> ArgumentError: invalid date
def to_datetime
unless blank?
date_values = ::Date._parse(self, false).
diff --git a/activesupport/lib/active_support/core_ext/string/exclude.rb b/activesupport/lib/active_support/core_ext/string/exclude.rb
index 5e184ec1b3..114bcb87f0 100644
--- a/activesupport/lib/active_support/core_ext/string/exclude.rb
+++ b/activesupport/lib/active_support/core_ext/string/exclude.rb
@@ -1,5 +1,10 @@
class String
- # The inverse of <tt>String#include?</tt>. Returns true if the string does not include the other string.
+ # The inverse of <tt>String#include?</tt>. Returns true if the string
+ # does not include the other string.
+ #
+ # "hello".exclude? "lo" #=> false
+ # "hello".exclude? "ol" #=> true
+ # "hello".exclude? ?h #=> false
def exclude?(string)
!include?(string)
end
diff --git a/activesupport/lib/active_support/core_ext/string/filters.rb b/activesupport/lib/active_support/core_ext/string/filters.rb
index 32a37296d5..2478f42290 100644
--- a/activesupport/lib/active_support/core_ext/string/filters.rb
+++ b/activesupport/lib/active_support/core_ext/string/filters.rb
@@ -5,7 +5,6 @@ class String
# the string, and then changing remaining consecutive whitespace
# groups into one space each.
#
- # Examples:
# %{ Multi-line
# string }.squish # => "Multi-line string"
# " foo bar \n \t boo".squish # => "foo bar boo"
diff --git a/activesupport/lib/active_support/core_ext/string/inflections.rb b/activesupport/lib/active_support/core_ext/string/inflections.rb
index 049ffe7986..070bfd7af6 100644
--- a/activesupport/lib/active_support/core_ext/string/inflections.rb
+++ b/activesupport/lib/active_support/core_ext/string/inflections.rb
@@ -13,7 +13,6 @@ class String
# the singular form will be returned if <tt>count == 1</tt>.
# For any other value of +count+ the plural will be returned.
#
- # ==== Examples
# 'post'.pluralize # => "posts"
# 'octopus'.pluralize # => "octopi"
# 'sheep'.pluralize # => "sheep"
@@ -46,7 +45,6 @@ class String
# in the string. It raises a NameError when the name is not in CamelCase
# or is not initialized. See ActiveSupport::Inflector.constantize
#
- # Examples
# 'Module'.constantize # => Module
# 'Class'.constantize # => Class
# 'blargle'.constantize # => NameError: wrong constant name blargle
@@ -58,7 +56,6 @@ class String
# in the string. It returns nil when the name is not in CamelCase
# or is not initialized. See ActiveSupport::Inflector.safe_constantize
#
- # Examples
# 'Module'.safe_constantize # => Module
# 'Class'.safe_constantize # => Class
# 'blargle'.safe_constantize # => nil
@@ -140,8 +137,6 @@ class String
# Replaces special characters in a string so that it may be used as part of a 'pretty' URL.
#
- # ==== Examples
- #
# class Person
# def to_param
# "#{id}-#{name.parameterize}"
@@ -194,7 +189,6 @@ class String
# +separate_class_name_and_id_with_underscore+ sets whether
# the method should put '_' between the name and 'id'.
#
- # Examples
# 'Message'.foreign_key # => "message_id"
# 'Message'.foreign_key(false) # => "messageid"
# 'Admin::Post'.foreign_key # => "post_id"
diff --git a/activesupport/lib/active_support/core_ext/string/output_safety.rb b/activesupport/lib/active_support/core_ext/string/output_safety.rb
index 215ba87ca9..6bda970e40 100644
--- a/activesupport/lib/active_support/core_ext/string/output_safety.rb
+++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb
@@ -14,7 +14,6 @@ class ERB
# In your ERB templates, use this method to escape any unsafe content. For example:
# <%=h @person.name %>
#
- # ==== Example:
# puts html_escape('is a > 0 & a < 10?')
# # => is a &gt; 0 &amp; a &lt; 10?
def html_escape(s)
@@ -37,7 +36,6 @@ class ERB
# A utility method for escaping HTML without affecting existing escaped entities.
#
- # ==== Examples
# html_escape_once('1 < 2 &amp; 3')
# # => "1 &lt; 2 &amp; 3"
#
diff --git a/activesupport/lib/active_support/i18n.rb b/activesupport/lib/active_support/i18n.rb
index f9c5e5e2f8..188653bd9b 100644
--- a/activesupport/lib/active_support/i18n.rb
+++ b/activesupport/lib/active_support/i18n.rb
@@ -6,4 +6,5 @@ rescue LoadError => e
raise e
end
+ActiveSupport.run_load_hooks(:i18n)
I18n.load_path << "#{File.dirname(__FILE__)}/locale/en.yml"
diff --git a/activesupport/lib/active_support/railtie.rb b/activesupport/lib/active_support/railtie.rb
index d1c62c5087..30ac881090 100644
--- a/activesupport/lib/active_support/railtie.rb
+++ b/activesupport/lib/active_support/railtie.rb
@@ -24,5 +24,12 @@ module ActiveSupport
Time.zone_default = zone_default
end
+
+ initializer "active_support.set_configs" do |app|
+ app.config.active_support.each do |k, v|
+ k = "#{k}="
+ ActiveSupport.send(k, v) if ActiveSupport.respond_to? k
+ end
+ end
end
end
diff --git a/activesupport/lib/active_support/ruby/shim.rb b/activesupport/lib/active_support/ruby/shim.rb
deleted file mode 100644
index 13e96b3596..0000000000
--- a/activesupport/lib/active_support/ruby/shim.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-# Backported Ruby builtins so you can code with the latest & greatest
-# but still run on any Ruby 1.8.x.
-#
-# Date next_year, next_month
-# DateTime to_date, to_datetime, xmlschema
-# Enumerable group_by, none?
-# String ord
-# Time to_date, to_time, to_datetime
-require 'active_support'
-require 'active_support/core_ext/date/calculations'
-require 'active_support/core_ext/date_time/conversions'
-require 'active_support/core_ext/enumerable'
-require 'active_support/core_ext/string/conversions'
-require 'active_support/core_ext/string/interpolation'
-require 'active_support/core_ext/time/conversions'
diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb
index 244ee1a224..f6bf0318f7 100644
--- a/activesupport/lib/active_support/testing/performance.rb
+++ b/activesupport/lib/active_support/testing/performance.rb
@@ -61,7 +61,7 @@ module ActiveSupport
ensure
begin
teardown
- run_callbacks :teardown, :enumerator => :reverse_each
+ run_callbacks :teardown
rescue Exception => e
result = @runner.puke(self.class, method_name, e)
end
diff --git a/activesupport/lib/active_support/time.rb b/activesupport/lib/active_support/time.rb
index 9634b52ecf..bcd5d78b54 100644
--- a/activesupport/lib/active_support/time.rb
+++ b/activesupport/lib/active_support/time.rb
@@ -4,10 +4,6 @@ module ActiveSupport
autoload :Duration, 'active_support/duration'
autoload :TimeWithZone, 'active_support/time_with_zone'
autoload :TimeZone, 'active_support/values/time_zone'
-
- on_load_all do
- [Duration, TimeWithZone, TimeZone]
- end
end
require 'date'
diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb
index 120b2a4c28..cd07c24257 100644
--- a/activesupport/lib/active_support/time_with_zone.rb
+++ b/activesupport/lib/active_support/time_with_zone.rb
@@ -35,8 +35,10 @@ module ActiveSupport
# t.is_a?(ActiveSupport::TimeWithZone) # => true
#
class TimeWithZone
+
+ # Report class name as 'Time' to thwart type checking
def self.name
- 'Time' # Report class name as 'Time' to thwart type checking
+ 'Time'
end
include Comparable
diff --git a/activesupport/test/abstract_unit.rb b/activesupport/test/abstract_unit.rb
index 40e25ce0cd..57ed4a6b60 100644
--- a/activesupport/test/abstract_unit.rb
+++ b/activesupport/test/abstract_unit.rb
@@ -7,9 +7,6 @@ ensure
$VERBOSE = old
end
-lib = File.expand_path("#{File.dirname(__FILE__)}/../lib")
-$:.unshift(lib) unless $:.include?('lib') || $:.include?(lib)
-
require 'active_support/core_ext/kernel/reporting'
require 'active_support/core_ext/string/encoding'
diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb
index bb9ce23276..d62b782e2d 100644
--- a/activesupport/test/caching_test.rb
+++ b/activesupport/test/caching_test.rb
@@ -684,6 +684,13 @@ class MemoryStoreTest < ActiveSupport::TestCase
assert @cache.exist?(2)
assert !@cache.exist?(1)
end
+
+ def test_write_with_unless_exist
+ assert_equal true, @cache.write(1, "aaaaaaaaaa")
+ assert_equal false, @cache.write(1, "aaaaaaaaaa", :unless_exist => true)
+ @cache.write(1, nil)
+ assert_equal false, @cache.write(1, "aaaaaaaaaa", :unless_exist => true)
+ end
end
uses_memcached 'memcached backed store' do
diff --git a/activesupport/test/callback_inheritance_test.rb b/activesupport/test/callback_inheritance_test.rb
index e5ac9511df..6be8ea8b84 100644
--- a/activesupport/test/callback_inheritance_test.rb
+++ b/activesupport/test/callback_inheritance_test.rb
@@ -29,7 +29,7 @@ class GrandParent
end
def dispatch
- run_callbacks(:dispatch, action_name) do
+ run_callbacks :dispatch do
@log << action_name
end
self
diff --git a/activesupport/test/callbacks_test.rb b/activesupport/test/callbacks_test.rb
index 25688a9da5..b7c3b130c3 100644
--- a/activesupport/test/callbacks_test.rb
+++ b/activesupport/test/callbacks_test.rb
@@ -112,7 +112,7 @@ module CallbacksTest
end
def dispatch
- run_callbacks :dispatch, action_name do
+ run_callbacks :dispatch do
@logger << "Done"
end
self
@@ -153,7 +153,7 @@ module CallbacksTest
end
def save
- run_callbacks :save, :action
+ run_callbacks :save
end
end
@@ -338,7 +338,7 @@ module CallbacksTest
end
def save
- run_callbacks :save, "hyphen-ated" do
+ run_callbacks :save do
@stuff
end
end
diff --git a/activesupport/test/core_ext/deep_dup_test.rb b/activesupport/test/core_ext/deep_dup_test.rb
new file mode 100644
index 0000000000..91d558dbb5
--- /dev/null
+++ b/activesupport/test/core_ext/deep_dup_test.rb
@@ -0,0 +1,53 @@
+require 'abstract_unit'
+require 'active_support/core_ext/object'
+
+class DeepDupTest < ActiveSupport::TestCase
+
+ def test_array_deep_dup
+ array = [1, [2, 3]]
+ dup = array.deep_dup
+ dup[1][2] = 4
+ assert_equal nil, array[1][2]
+ assert_equal 4, dup[1][2]
+ end
+
+ def test_hash_deep_dup
+ hash = { :a => { :b => 'b' } }
+ dup = hash.deep_dup
+ dup[:a][:c] = 'c'
+ assert_equal nil, hash[:a][:c]
+ assert_equal 'c', dup[:a][:c]
+ end
+
+ def test_array_deep_dup_with_hash_inside
+ array = [1, { :a => 2, :b => 3 } ]
+ dup = array.deep_dup
+ dup[1][:c] = 4
+ assert_equal nil, array[1][:c]
+ assert_equal 4, dup[1][:c]
+ end
+
+ def test_hash_deep_dup_with_array_inside
+ hash = { :a => [1, 2] }
+ dup = hash.deep_dup
+ dup[:a][2] = 'c'
+ assert_equal nil, hash[:a][2]
+ assert_equal 'c', dup[:a][2]
+ end
+
+ def test_deep_dup_initialize
+ zero_hash = Hash.new 0
+ hash = { :a => zero_hash }
+ dup = hash.deep_dup
+ assert_equal 0, dup[:a][44]
+ end
+
+ def test_object_deep_dup
+ object = Object.new
+ dup = object.deep_dup
+ dup.instance_variable_set(:@a, 1)
+ assert !object.instance_variable_defined?(:@a)
+ assert dup.instance_variable_defined?(:@a)
+ end
+
+end
diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb
index 80b3c16328..8239054117 100644
--- a/activesupport/test/core_ext/hash_ext_test.rb
+++ b/activesupport/test/core_ext/hash_ext_test.rb
@@ -363,21 +363,6 @@ class HashExtTest < ActiveSupport::TestCase
assert_equal expected, hash_1
end
- def test_deep_dup
- hash = { :a => { :b => 'b' } }
- dup = hash.deep_dup
- dup[:a][:c] = 'c'
- assert_equal nil, hash[:a][:c]
- assert_equal 'c', dup[:a][:c]
- end
-
- def test_deep_dup_initialize
- zero_hash = Hash.new 0
- hash = { :a => zero_hash }
- dup = hash.deep_dup
- assert_equal 0, dup[:a][44]
- end
-
def test_store_on_indifferent_access
hash = HashWithIndifferentAccess.new
hash.store(:test1, 1)
@@ -506,15 +491,21 @@ class HashExtTest < ActiveSupport::TestCase
original = { :a => 'x', :b => 'y', :c => 10 }
expected = { :a => 'x', :b => 'y' }
- # Should return a new hash with only the given keys.
+ # Should return a new hash without the given keys.
assert_equal expected, original.except(:c)
assert_not_equal expected, original
- # Should replace the hash with only the given keys.
+ # Should replace the hash without the given keys.
assert_equal expected, original.except!(:c)
assert_equal expected, original
end
+ def test_except_with_more_than_one_argument
+ original = { :a => 'x', :b => 'y', :c => 10 }
+ expected = { :a => 'x' }
+ assert_equal expected, original.except(:b, :c)
+ end
+
def test_except_with_original_frozen
original = { :a => 'x', :b => 'y' }
original.freeze
diff --git a/activesupport/test/core_ext/object_and_class_ext_test.rb b/activesupport/test/core_ext/object_and_class_ext_test.rb
index b027fccab3..98ab82609e 100644
--- a/activesupport/test/core_ext/object_and_class_ext_test.rb
+++ b/activesupport/test/core_ext/object_and_class_ext_test.rb
@@ -101,7 +101,7 @@ class ObjectTryTest < ActiveSupport::TestCase
assert !@string.respond_to?(method)
assert_raise(NoMethodError) { @string.try(method) }
end
-
+
def test_nonexisting_method_with_arguments
method = :undefined_method
assert !@string.respond_to?(method)
@@ -138,4 +138,16 @@ class ObjectTryTest < ActiveSupport::TestCase
nil.try { ran = true }
assert_equal false, ran
end
+
+ def test_try_with_private_method
+ klass = Class.new do
+ private
+
+ def private_method
+ 'private method'
+ end
+ end
+
+ assert_raise(NoMethodError) { klass.new.try(:private_method) }
+ end
end
diff --git a/activesupport/test/core_ext/range_ext_test.rb b/activesupport/test/core_ext/range_ext_test.rb
index cf1ec448c2..9c3389ba82 100644
--- a/activesupport/test/core_ext/range_ext_test.rb
+++ b/activesupport/test/core_ext/range_ext_test.rb
@@ -41,6 +41,18 @@ class RangeTest < ActiveSupport::TestCase
assert((1..10).include?(1...10))
end
+ def test_should_compare_identical_inclusive
+ assert((1..10) === (1..10))
+ end
+
+ def test_should_compare_identical_exclusive
+ assert((1...10) === (1...10))
+ end
+
+ def test_should_compare_other_with_exlusive_end
+ assert((1..10) === (1...10))
+ end
+
def test_exclusive_end_should_not_include_identical_with_inclusive_end
assert !(1...10).include?(1..10)
end
diff --git a/activesupport/test/ts_isolated.rb b/activesupport/test/ts_isolated.rb
index 1d96c20bb6..938bb4ee99 100644
--- a/activesupport/test/ts_isolated.rb
+++ b/activesupport/test/ts_isolated.rb
@@ -1,5 +1,3 @@
-$:.unshift(File.dirname(__FILE__) + '/../../activesupport/lib')
-
require 'minitest/autorun'
require 'active_support/test_case'
require 'rbconfig'
diff --git a/guides/code/getting_started/public/robots.txt b/guides/code/getting_started/public/robots.txt
index 085187fa58..1a3a5e4dd2 100644
--- a/guides/code/getting_started/public/robots.txt
+++ b/guides/code/getting_started/public/robots.txt
@@ -1,5 +1,5 @@
# See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file
#
# To ban all spiders from the entire site uncomment the next two lines:
-# User-Agent: *
+# User-agent: *
# Disallow: /
diff --git a/guides/code/getting_started/test/performance/browsing_test.rb b/guides/code/getting_started/test/performance/browsing_test.rb
index 3fea27b916..2a849b7f2b 100644
--- a/guides/code/getting_started/test/performance/browsing_test.rb
+++ b/guides/code/getting_started/test/performance/browsing_test.rb
@@ -3,7 +3,7 @@ require 'rails/performance_test_help'
class BrowsingTest < ActionDispatch::PerformanceTest
# Refer to the documentation for all available options
- # self.profile_options = { :runs => 5, :metrics => [:wall_time, :memory]
+ # self.profile_options = { :runs => 5, :metrics => [:wall_time, :memory],
# :output => 'tmp/performance', :formats => [:flat] }
def test_homepage
diff --git a/guides/rails_guides/textile_extensions.rb b/guides/rails_guides/textile_extensions.rb
index 4677fae504..0a002a785f 100644
--- a/guides/rails_guides/textile_extensions.rb
+++ b/guides/rails_guides/textile_extensions.rb
@@ -1,5 +1,11 @@
require 'active_support/core_ext/object/inclusion'
+module RedCloth::Formatters::HTML
+ def emdash(opts)
+ "--"
+ end
+end
+
module RailsGuides
module TextileExtensions
def notestuff(body)
diff --git a/guides/source/active_record_querying.textile b/guides/source/active_record_querying.textile
index f9dbaa1125..a9cb424eaa 100644
--- a/guides/source/active_record_querying.textile
+++ b/guides/source/active_record_querying.textile
@@ -99,9 +99,28 @@ SELECT * FROM clients WHERE (clients.id = 10) LIMIT 1
<tt>Model.find(primary_key)</tt> will raise an +ActiveRecord::RecordNotFound+ exception if no matching record is found.
+h5. +take+
+
+<tt>Model.take</tt> retrieves a record without any implicit ordering. For example:
+
+<ruby>
+client = Client.take
+# => #<Client id: 1, first_name: "Lifo">
+</ruby>
+
+The SQL equivalent of the above is:
+
+<sql>
+SELECT * FROM clients LIMIT 1
+</sql>
+
+<tt>Model.take</tt> returns +nil+ if no record is found and no exception will be raised.
+
+TIP: The retrieved record may vary depending on the database engine.
+
h5. +first+
-<tt>Model.first</tt> finds the first record matched by the supplied options, if any. For example:
+<tt>Model.first</tt> finds the first record ordered by the primary key. For example:
<ruby>
client = Client.first
@@ -111,14 +130,14 @@ client = Client.first
The SQL equivalent of the above is:
<sql>
-SELECT * FROM clients LIMIT 1
+SELECT * FROM clients ORDER BY clients.id ASC LIMIT 1
</sql>
-<tt>Model.first</tt> returns +nil+ if no matching record is found. No exception will be raised.
+<tt>Model.first</tt> returns +nil+ if no matching record is found and no exception will be raised.
h5. +last+
-<tt>Model.last</tt> finds the last record matched by the supplied options. For example:
+<tt>Model.last</tt> finds the last record ordered by the primary key. For example:
<ruby>
client = Client.last
@@ -131,7 +150,7 @@ The SQL equivalent of the above is:
SELECT * FROM clients ORDER BY clients.id DESC LIMIT 1
</sql>
-<tt>Model.last</tt> returns +nil+ if no matching record is found. No exception will be raised.
+<tt>Model.last</tt> returns +nil+ if no matching record is found and no exception will be raised.
h5. +find_by+
@@ -148,12 +167,29 @@ Client.find_by first_name: 'Jon'
It is equivalent to writing:
<ruby>
-Client.where(first_name: 'Lifo').first
+Client.where(first_name: 'Lifo').take
</ruby>
+h5(#take_1). +take!+
+
+<tt>Model.take!</tt> retrieves a record without any implicit ordering. For example:
+
+<ruby>
+client = Client.take!
+# => #<Client id: 1, first_name: "Lifo">
+</ruby>
+
+The SQL equivalent of the above is:
+
+<sql>
+SELECT * FROM clients LIMIT 1
+</sql>
+
+<tt>Model.take!</tt> raises +ActiveRecord::RecordNotFound+ if no matching record is found.
+
h5(#first_1). +first!+
-<tt>Model.first!</tt> finds the first record. For example:
+<tt>Model.first!</tt> finds the first record ordered by the primary key. For example:
<ruby>
client = Client.first!
@@ -163,14 +199,14 @@ client = Client.first!
The SQL equivalent of the above is:
<sql>
-SELECT * FROM clients LIMIT 1
+SELECT * FROM clients ORDER BY clients.id ASC LIMIT 1
</sql>
-<tt>Model.first!</tt> raises +RecordNotFound+ if no matching record is found.
+<tt>Model.first!</tt> raises +ActiveRecord::RecordNotFound+ if no matching record is found.
h5(#last_1). +last!+
-<tt>Model.last!</tt> finds the last record. For example:
+<tt>Model.last!</tt> finds the last record ordered by the primary key. For example:
<ruby>
client = Client.last!
@@ -183,24 +219,24 @@ The SQL equivalent of the above is:
SELECT * FROM clients ORDER BY clients.id DESC LIMIT 1
</sql>
-<tt>Model.last!</tt> raises +RecordNotFound+ if no matching record is found.
+<tt>Model.last!</tt> raises +ActiveRecord::RecordNotFound+ if no matching record is found.
h5(#find_by_1). +find_by!+
-<tt>Model.find_by!</tt> finds the first record matching some conditions. It raises +RecordNotFound+ if no matching record is found. For example:
+<tt>Model.find_by!</tt> finds the first record matching some conditions. It raises +ActiveRecord::RecordNotFound+ if no matching record is found. For example:
<ruby>
Client.find_by! first_name: 'Lifo'
# => #<Client id: 1, first_name: "Lifo">
Client.find_by! first_name: 'Jon'
-# => RecordNotFound
+# => ActiveRecord::RecordNotFound
</ruby>
It is equivalent to writing:
<ruby>
-Client.where(first_name: 'Lifo').first!
+Client.where(first_name: 'Lifo').take!
</ruby>
h4. Retrieving Multiple Objects
diff --git a/guides/source/active_support_core_extensions.textile b/guides/source/active_support_core_extensions.textile
index e4a6e145b9..8045316e98 100644
--- a/guides/source/active_support_core_extensions.textile
+++ b/guides/source/active_support_core_extensions.textile
@@ -154,6 +154,51 @@ WARNING. Any class can disallow duplication removing +dup+ and +clone+ or raisin
NOTE: Defined in +active_support/core_ext/object/duplicable.rb+.
+h4. +deep_dup+
+
+The +deep_dup+ method returns deep copy of given object. Normally, when you +dup+ an object that contains other objects, ruby does not +dup+ them. If you have array with a string, for example, it will look like this:
+
+<ruby>
+array = ['string']
+duplicate = array.dup
+
+duplicate.push 'another-string'
+
+# object was duplicated, element added only to duplicate
+array #=> ['string']
+duplicate #=> ['string', 'another-string']
+
+duplicate.first.gsub!('string', 'foo')
+
+# first element was not duplicated, it will be changed for both arrays
+array #=> ['foo']
+duplicate #=> ['foo', 'another-string']
+</ruby>
+
+As you can see, after duplicating +Array+ instance, we got another object, therefore we can modify it and the original object will stay unchanged. This is not true for array's elements, however. Since +dup+ does not make deep copy, the string inside array is still the same object.
+
+If you need a deep copy of an object, you should use +deep_dup+ in such situation:
+
+<ruby>
+array = ['string']
+duplicate = array.deep_dup
+
+duplicate.first.gsub!('string', 'foo')
+
+array #=> ['string']
+duplicate #=> ['foo']
+</ruby>
+
+If object is not duplicable +deep_dup+ will just return this object:
+
+<ruby>
+number = 1
+dup = number.deep_dup
+number.object_id == dup.object_id # => true
+</ruby>
+
+NOTE: Defined in +active_support/core_ext/object/deep_dup.rb+.
+
h4. +try+
Sometimes you want to call a method provided the receiver object is not +nil+, which is something you usually check first. +try+ is like +Object#send+ except that it returns +nil+ if sent to +nil+.
@@ -184,7 +229,7 @@ You can evaluate code in the context of any object's singleton class using +clas
<ruby>
class Proc
def bind(object)
- block, time = self, Time.now
+ block, time = self, Time.current
object.class_eval do
method_name = "__bind_#{time.to_i}_#{time.usec}"
define_method(method_name, &block)
@@ -1034,49 +1079,6 @@ A model may find it useful to set +:instance_accessor+ to +false+ as a way to pr
NOTE: Defined in +active_support/core_ext/class/attribute_accessors.rb+.
-h4. Class Inheritable Attributes
-
-WARNING: Class Inheritable Attributes are deprecated. It's recommended that you use +Class#class_attribute+ instead.
-
-Class variables are shared down the inheritance tree. Class instance variables are not shared, but they are not inherited either. The macros +class_inheritable_reader+, +class_inheritable_writer+, and +class_inheritable_accessor+ provide accessors for class-level data which is inherited but not shared with children:
-
-<ruby>
-module ActionController
- class Base
- # FIXME: REVISE/SIMPLIFY THIS COMMENT.
- # The value of allow_forgery_protection is inherited,
- # but its value in a particular class does not affect
- # the value in the rest of the controllers hierarchy.
- class_inheritable_accessor :allow_forgery_protection
- end
-end
-</ruby>
-
-They accomplish this with class instance variables and cloning on subclassing, there are no class variables involved. Cloning is performed with +dup+ as long as the value is duplicable.
-
-There are some variants specialised in arrays and hashes:
-
-<ruby>
-class_inheritable_array
-class_inheritable_hash
-</ruby>
-
-Those writers take any inherited array or hash into account and extend them rather than overwrite them.
-
-As with vanilla class attribute accessors these macros create convenience instance methods for reading and writing. The generation of the writer instance method can be prevented setting +:instance_writer+ to +false+ (not any false value, but exactly +false+):
-
-<ruby>
-module ActiveRecord
- class Base
- class_inheritable_accessor :default_scoping, :instance_writer => false
- end
-end
-</ruby>
-
-Since values are copied when a subclass is defined, if the base class changes the attribute after that, the subclass does not see the new value. That's the point.
-
-NOTE: Defined in +active_support/core_ext/class/inheritable_attributes.rb+.
-
h4. Subclasses & Descendants
h5. +subclasses+
@@ -1255,9 +1257,14 @@ Pass a +:separator+ to truncate the string at a natural break:
# => "Oh dear! Oh..."
</ruby>
-In the above example "dear" gets cut first, but then +:separator+ prevents it.
+The option +:separator+ can be a regexp:
+
+<ruby>
+"Oh dear! Oh dear! I shall be late!".truncate(18, :separator => /\s/)
+# => "Oh dear! Oh..."
+</ruby>
-WARNING: The option +:separator+ can't be a regexp.
+In above examples "dear" gets cut first, but then +:separator+ prevents it.
NOTE: Defined in +active_support/core_ext/string/filters.rb+.
@@ -1810,6 +1817,43 @@ Singular forms are aliased so you are able to say:
NOTE: Defined in +active_support/core_ext/numeric/bytes.rb+.
+h4. Time
+
+Enables the use of time calculations and declarations, like 45.minutes + 2.hours + 4.years.
+
+These methods use Time#advance for precise date calculations when using from_now, ago, etc.
+as well as adding or subtracting their results from a Time object. For example:
+
+<ruby>
+# equivalent to Time.current.advance(:months => 1)
+1.month.from_now
+
+# equivalent to Time.current.advance(:years => 2)
+2.years.from_now
+
+# equivalent to Time.current.advance(:months => 4, :years => 5)
+(4.months + 5.years).from_now
+</ruby>
+
+While these methods provide precise calculation when used as in the examples above, care
+should be taken to note that this is not true if the result of `months', `years', etc is
+converted before use:
+
+<ruby>
+# equivalent to 30.days.to_i.from_now
+1.month.to_i.from_now
+
+# equivalent to 365.25.days.to_f.from_now
+1.year.to_f.from_now
+</ruby>
+
+In such cases, Ruby's core
+Date[http://ruby-doc.org/stdlib/libdoc/date/rdoc/Date.html] and
+Time[http://ruby-doc.org/stdlib/libdoc/time/rdoc/Time.html] should be used for precision
+date and time arithmetic.
+
+NOTE: Defined in +active_support/core_ext/numeric/time.rb+.
+
h3. Extensions to +Integer+
h4. +multiple_of?+
@@ -2103,20 +2147,20 @@ To do so it sends +to_xml+ to every item in turn, and collects the results under
By default, the name of the root element is the underscorized and dasherized plural of the name of the class of the first item, provided the rest of elements belong to that type (checked with <tt>is_a?</tt>) and they are not hashes. In the example above that's "contributors".
-If there's any element that does not belong to the type of the first one the root node becomes "records":
+If there's any element that does not belong to the type of the first one the root node becomes "objects":
<ruby>
[Contributor.first, Commit.first].to_xml
# =>
# <?xml version="1.0" encoding="UTF-8"?>
-# <records type="array">
-# <record>
+# <objects type="array">
+# <object>
# <id type="integer">4583</id>
# <name>Aaron Batalion</name>
# <rank type="integer">53</rank>
# <url-id>aaron-batalion</url-id>
-# </record>
-# <record>
+# </object>
+# <object>
# <author>Joshua Peek</author>
# <authored-timestamp type="datetime">2009-09-02T16:44:36Z</authored-timestamp>
# <branch>origin/master</branch>
@@ -2127,30 +2171,30 @@ If there's any element that does not belong to the type of the first one the roo
# <imported-from-svn type="boolean">false</imported-from-svn>
# <message>Kill AMo observing wrap_with_notifications since ARes was only using it</message>
# <sha1>723a47bfb3708f968821bc969a9a3fc873a3ed58</sha1>
-# </record>
-# </records>
+# </object>
+# </objects>
</ruby>
-If the receiver is an array of hashes the root element is by default also "records":
+If the receiver is an array of hashes the root element is by default also "objects":
<ruby>
[{:a => 1, :b => 2}, {:c => 3}].to_xml
# =>
# <?xml version="1.0" encoding="UTF-8"?>
-# <records type="array">
-# <record>
+# <objects type="array">
+# <object>
# <b type="integer">2</b>
# <a type="integer">1</a>
-# </record>
-# <record>
+# </object>
+# <object>
# <c type="integer">3</c>
-# </record>
-# </records>
+# </object>
+# </objects>
</ruby>
WARNING. If the collection is empty the root element is by default "nil-classes". That's a gotcha, for example the root element of the list of contributors above would not be "contributors" if the collection was empty, but "nil-classes". You may use the <tt>:root</tt> option to ensure a consistent root element.
-The name of children nodes is by default the name of the root node singularized. In the examples above we've seen "contributor" and "record". The option <tt>:children</tt> allows you to set these node names.
+The name of children nodes is by default the name of the root node singularized. In the examples above we've seen "contributor" and "object". The option <tt>:children</tt> allows you to set these node names.
The default XML builder is a fresh instance of <tt>Builder::XmlMarkup</tt>. You can configure your own builder via the <tt>:builder</tt> option. The method also accepts options like <tt>:dasherize</tt> and friends, they are forwarded to the builder:
@@ -2217,6 +2261,19 @@ Thus, in this case the behavior is different for +nil+, and the differences with
NOTE: Defined in +active_support/core_ext/array/wrap.rb+.
+h4. Duplicating
+
+The method +Array.deep_dup+ duplicates itself and all objects inside recursively with ActiveSupport method +Object#deep_dup+. It works like +Array#map+ with sending +deep_dup+ method to each object inside.
+
+<ruby>
+array = [1, [2, 3]]
+dup = array.deep_dup
+dup[1][2] = 4
+array[1][2] == nil # => true
+</ruby>
+
+NOTE: Defined in +active_support/core_ext/array/deep_dup.rb+.
+
h4. Grouping
h5. +in_groups_of(number, fill_with = nil)+
@@ -2423,6 +2480,23 @@ The method +deep_merge!+ performs a deep merge in place.
NOTE: Defined in +active_support/core_ext/hash/deep_merge.rb+.
+h4. Deep duplicating
+
+The method +Hash.deep_dup+ duplicates itself and all keys and values inside recursively with ActiveSupport method +Object#deep_dup+. It works like +Enumerator#each_with_object+ with sending +deep_dup+ method to each pair inside.
+
+<ruby>
+hash = { :a => 1, :b => { :c => 2, :d => [3, 4] } }
+
+dup = hash.deep_dup
+dup[:b][:e] = 5
+dup[:b][:d] << 5
+
+hash[:b][:e] == nil # => true
+hash[:b][:d] == [3, 4] # => true
+</ruby>
+
+NOTE: Defined in +active_support/core_ext/hash/deep_dup.rb+.
+
h4. Diffing
The method +diff+ returns a hash that represents a diff of the receiver and the argument with the following logic:
@@ -2707,22 +2781,25 @@ NOTE: Defined in +active_support/core_ext/range/blockless_step.rb+.
h4. +include?+
-The method +Range#include?+ says whether some value falls between the ends of a given instance:
+The methods +Range#include?+ and +Range#===+ say whether some value falls between the ends of a given instance:
<ruby>
(2..3).include?(Math::E) # => true
</ruby>
-Active Support extends this method so that the argument may be another range in turn. In that case we test whether the ends of the argument range belong to the receiver themselves:
+Active Support extends these methods so that the argument may be another range in turn. In that case we test whether the ends of the argument range belong to the receiver themselves:
<ruby>
(1..10).include?(3..7) # => true
(1..10).include?(0..7) # => false
(1..10).include?(3..11) # => false
(1...9).include?(3..9) # => false
-</ruby>
-WARNING: The original +Range#include?+ is still the one aliased to +Range#===+.
+(1..10) === (3..7) # => true
+(1..10) === (0..7) # => false
+(1..10) === (3..11) # => false
+(1...9) === (3..9) # => false
+</ruby>
NOTE: Defined in +active_support/core_ext/range/include_range.rb+.
@@ -3052,18 +3129,38 @@ The method +beginning_of_day+ returns a timestamp at the beginning of the day (0
<ruby>
date = Date.new(2010, 6, 7)
-date.beginning_of_day # => Sun Jun 07 00:00:00 +0200 2010
+date.beginning_of_day # => Mon Jun 07 00:00:00 +0200 2010
</ruby>
The method +end_of_day+ returns a timestamp at the end of the day (23:59:59):
<ruby>
date = Date.new(2010, 6, 7)
-date.end_of_day # => Sun Jun 06 23:59:59 +0200 2010
+date.end_of_day # => Mon Jun 07 23:59:59 +0200 2010
</ruby>
+beginning_of_day+ is aliased to +at_beginning_of_day+, +midnight+, +at_midnight+.
+h6. +beginning_of_hour+, +end_of_hour+
+
+The method +beginning_of_hour+ returns a timestamp at the beginning of the hour (hh:00:00):
+
+<ruby>
+date = DateTime.new(2010, 6, 7, 19, 55, 25)
+date.beginning_of_hour # => Mon Jun 07 19:00:00 +0200 2010
+</ruby>
+
+The method +end_of_hour+ returns a timestamp at the end of the hour (hh:59:59):
+
+<ruby>
+date = DateTime.new(2010, 6, 7, 19, 55, 25)
+date.end_of_hour # => Mon Jun 07 19:59:59 +0200 2010
+</ruby>
+
++beginning_of_hour+ is aliased to +at_beginning_of_hour+.
+
+INFO: +beginning_of_hour+ and +end_of_hour+ are implemented for +Time+ and +DateTime+ but *not* +Date+ as it does not make sense to request the beginning or end of an hour on a +Date+ instance.
+
h6. +ago+, +since+
The method +ago+ receives a number of seconds as argument and returns a timestamp those many seconds ago from midnight:
@@ -3131,6 +3228,13 @@ since (in)
On the other hand, +advance+ and +change+ are also defined and support more options, they are documented below.
+The following methods are only implemented in +active_support/core_ext/date_time/calculations.rb+ as they only make sense when used with a +DateTime+ instance:
+
+<ruby>
+beginning_of_hour (at_beginning_of_hour)
+end_of_hour
+</ruby>
+
h5. Named Datetimes
h6. +DateTime.current+
@@ -3273,6 +3377,8 @@ ago
since (in)
beginning_of_day (midnight, at_midnight, at_beginning_of_day)
end_of_day
+beginning_of_hour (at_beginning_of_hour)
+end_of_hour
beginning_of_week (at_beginning_of_week)
end_of_week (at_end_of_week)
monday
diff --git a/guides/source/caching_with_rails.textile b/guides/source/caching_with_rails.textile
index e455b504ce..34a100cd3a 100644
--- a/guides/source/caching_with_rails.textile
+++ b/guides/source/caching_with_rails.textile
@@ -229,6 +229,42 @@ class ProductsController < ActionController
end
</ruby>
+Sometimes it is necessary to disambiguate the controller when you call +expire_action+, such as when there are two identically named controllers in separate namespaces:
+
+<ruby>
+class ProductsController < ActionController
+ caches_action :index
+
+ def index
+ @products = Product.all
+ end
+end
+
+module Admin
+ class ProductsController < ActionController
+ cache_sweeper :product_sweeper
+
+ def new
+ @product = Product.new
+ end
+
+ def create
+ @product = Product.create(params[:product])
+ end
+ end
+end
+
+class ProductSweeper < ActionController::Caching::Sweeper
+ observe Product
+
+ def after_create(product)
+ expire_action(:controller => '/products', :action => 'index')
+ end
+end
+</ruby>
+
+Note the use of '/products' here rather than 'products'. If you wanted to expire an action cache for the +Admin::ProductsController+, you would use 'admin/products' instead.
+
h4. SQL Caching
Query caching is a Rails feature that caches the result set returned by each query so that if Rails encounters the same query again for that request, it will use the cached result set as opposed to running the query against the database again.
diff --git a/guides/source/configuring.textile b/guides/source/configuring.textile
index 5629c82ca0..b2c9300034 100644
--- a/guides/source/configuring.textile
+++ b/guides/source/configuring.textile
@@ -186,13 +186,13 @@ The full set of methods that can be used in this block are as follows:
* +force_plural+ allows pluralized model names. Defaults to +false+.
* +helper+ defines whether or not to generate helpers. Defaults to +true+.
* +integration_tool+ defines which integration tool to use. Defaults to +nil+.
-* +javascripts+ turns on the hook for javascripts in generators. Used in Rails for when the +scaffold+ generator is ran. Defaults to +true+.
+* +javascripts+ turns on the hook for javascripts in generators. Used in Rails for when the +scaffold+ generator is run. Defaults to +true+.
* +javascript_engine+ configures the engine to be used (for eg. coffee) when generating assets. Defaults to +nil+.
* +orm+ defines which orm to use. Defaults to +false+ and will use Active Record by default.
* +performance_tool+ defines which performance tool to use. Defaults to +nil+.
* +resource_controller+ defines which generator to use for generating a controller when using +rails generate resource+. Defaults to +:controller+.
* +scaffold_controller+ different from +resource_controller+, defines which generator to use for generating a _scaffolded_ controller when using +rails generate scaffold+. Defaults to +:scaffold_controller+.
-* +stylesheets+ turns on the hook for stylesheets in generators. Used in Rails for when the +scaffold+ generator is ran, but this hook can be used in other generates as well. Defaults to +true+.
+* +stylesheets+ turns on the hook for stylesheets in generators. Used in Rails for when the +scaffold+ generator is run, but this hook can be used in other generates as well. Defaults to +true+.
* +stylesheet_engine+ configures the stylesheet engine (for eg. sass) to be used when generating assets. Defaults to +:css+.
* +test_framework+ defines which test framework to use. Defaults to +false+ and will use Test::Unit by default.
* +template_engine+ defines which template engine to use, such as ERB or Haml. Defaults to +:erb+.
@@ -448,9 +448,9 @@ There are a few configuration options available in Active Support:
* +config.active_support.bare+ enables or disables the loading of +active_support/all+ when booting Rails. Defaults to +nil+, which means +active_support/all+ is loaded.
-* +config.active_support.escape_html_entities_in_json+ enables or disables the escaping of HTML entities in JSON serialization. Defaults to +true+.
+* +config.active_support.escape_html_entities_in_json+ enables or disables the escaping of HTML entities in JSON serialization. Defaults to +false+.
-* +config.active_support.use_standard_json_time_format+ enables or disables serializing dates to ISO 8601 format. Defaults to +false+.
+* +config.active_support.use_standard_json_time_format+ enables or disables serializing dates to ISO 8601 format. Defaults to +true+.
* +ActiveSupport::BufferedLogger.silencer+ is set to +false+ to disable the ability to silence logging in a block. The default is +true+.
@@ -589,13 +589,13 @@ TIP: If you have any ordering dependency in your initializers, you can control t
h3. Initialization events
-Rails has 5 initialization events which can be hooked into (listed in the order that they are ran):
+Rails has 5 initialization events which can be hooked into (listed in the order that they are run):
* +before_configuration+: This is run as soon as the application constant inherits from +Rails::Application+. The +config+ calls are evaluated before this happens.
* +before_initialize+: This is run directly before the initialization process of the application occurs with the +:bootstrap_hook+ initializer near the beginning of the Rails initialization process.
-* +to_prepare+: Run after the initializers are ran for all Railties (including the application itself), but before eager loading and the middleware stack is built. More importantly, will run upon every request in +development+, but only once (during boot-up) in +production+ and +test+.
+* +to_prepare+: Run after the initializers are run for all Railties (including the application itself), but before eager loading and the middleware stack is built. More importantly, will run upon every request in +development+, but only once (during boot-up) in +production+ and +test+.
* +before_eager_load+: This is run directly before eager loading occurs, which is the default behaviour for the _production_ environment and not for the +development+ environment.
@@ -736,7 +736,7 @@ The error occurred while evaluating nil.each
*+load_config_initializers+* Loads all Ruby files from +config/initializers+ in the application, railties and engines. The files in this directory can be used to hold configuration settings that should be made after all of the frameworks are loaded.
-*+engines_blank_point+* Provides a point-in-initialization to hook into if you wish to do anything before engines are loaded. After this point, all railtie and engine initializers are ran.
+*+engines_blank_point+* Provides a point-in-initialization to hook into if you wish to do anything before engines are loaded. After this point, all railtie and engine initializers are run.
*+add_generator_templates+* Finds templates for generators at +lib/templates+ for the application, railities and engines and adds these to the +config.generators.templates+ setting, which will make the templates available for all generators to reference.
diff --git a/guides/source/debugging_rails_applications.textile b/guides/source/debugging_rails_applications.textile
index 903ed59e7b..45fa4ada78 100644
--- a/guides/source/debugging_rails_applications.textile
+++ b/guides/source/debugging_rails_applications.textile
@@ -124,7 +124,7 @@ h4. Log Levels
When something is logged it's printed into the corresponding log if the log level of the message is equal or higher than the configured log level. If you want to know the current log level you can call the +Rails.logger.level+ method.
-The available log levels are: +:debug+, +:info+, +:warn+, +:error+, and +:fatal+, corresponding to the log level numbers from 0 up to 4 respectively. To change the default log level, use
+The available log levels are: +:debug+, +:info+, +:warn+, +:error+, +:fatal+, and +:unknown+, corresponding to the log level numbers from 0 up to 5 respectively. To change the default log level, use
<ruby>
config.log_level = :warn # In any environment initializer, or
diff --git a/guides/source/engines.textile b/guides/source/engines.textile
index 71bcf6b713..880be57fb5 100644
--- a/guides/source/engines.textile
+++ b/guides/source/engines.textile
@@ -448,6 +448,8 @@ rake db:migrate SCOPE=blorgh VERSION=0
h4. Using a class provided by the application
+h5. Using a model provided by the application
+
When an engine is created, it may want to use specific classes from an application to provide links between the pieces of the engine and the pieces of the application. In the case of the +blorgh+ engine, making posts and comments have authors would make a lot of sense.
Usually, an application would have a +User+ class that would provide the objects that would represent the posts' and comments' authors, but there could be a case where the application calls this class something different, such as +Person+. It's because of this reason that the engine should not hardcode the associations to be exactly for a +User+ class, but should allow for some flexibility around what the class is called.
@@ -544,6 +546,19 @@ end
Now instead of the ugly Ruby object output the author's name will be displayed.
+h5. Using a controller provided by the application
+
+Because Rails controllers generally share code for things like authentication and accessing session variables, by default they inherit from <tt>ApplicationController</tt>. Rails engines, however are scoped to run independently from the main application, so each engine gets a scoped +ApplicationController+. This namespace prevents code collisions, but often engine controllers should access methods in the main application's +ApplicationController+. An easy way to provide this access is to change the engine's scoped +ApplicationController+ to inherit from the main application's +ApplicationController+. For our Blorgh engine this would be done by changing +app/controllers/blorgh/application_controller.rb+ to look like:
+
+<ruby>
+class Blorgh::ApplicationController < ApplicationController
+end
+</ruby>
+
+By default, the engine's controllers inherit from <tt>Blorgh::ApplicationController</tt>. So, after making this change they will have access to the main applications +ApplicationController+ as though they were part of the main application.
+
+This change does require that the engine is run from a Rails application that has an +ApplicationController+.
+
h4. Configuring an engine
This section covers firstly how you can make the +user_class+ setting of the Blorgh engine configurable, followed by general configuration tips for the engine.
diff --git a/guides/source/getting_started.textile b/guides/source/getting_started.textile
index 947abd7ba0..1e9bd1f144 100644
--- a/guides/source/getting_started.textile
+++ b/guides/source/getting_started.textile
@@ -717,7 +717,7 @@ If you reload
try to save a post without a title, Rails will send you back to the
form, but that's not very useful. You need to tell the user that
something went wrong. To do that, you'll modify
-+app/views/posts/index.html.erb+ to check for error messages:
++app/views/posts/new.html.erb+ to check for error messages:
<erb>
<%= form_for :post, :url => { :action => :create } do |f| %>
diff --git a/guides/source/index.html.erb b/guides/source/index.html.erb
index 5439459b42..74805b2754 100644
--- a/guides/source/index.html.erb
+++ b/guides/source/index.html.erb
@@ -13,7 +13,7 @@ Ruby on Rails Guides
and <%= link_to 'Free Kindle Reading Apps', 'http://www.amazon.com/gp/kindle/kcp' %> for the iPad,
iPhone, Mac, Android, etc. Download them from <%= link_to 'here', @mobi %>.
</dd>
- <dd class="work-in-progress">Guides marked with this icon are currently being worked on. While they might still be useful to you, they may contain incomplete information and even errors. You can help by reviewing them and posting your comments and corrections to the author.</dd>
+ <dd class="work-in-progress">Guides marked with this icon are currently being worked on and will not be available in the Guides Index menu. While still useful, they may contain incomplete information and even errors. You can help by reviewing them and posting your comments and corrections.</dd>
</dl>
</div>
<% end %>
diff --git a/guides/source/security.textile b/guides/source/security.textile
index ac64b82bf6..ac55d60368 100644
--- a/guides/source/security.textile
+++ b/guides/source/security.textile
@@ -627,7 +627,7 @@ h4. Whitelists versus Blacklists
-- _When sanitizing, protecting or verifying something, whitelists over blacklists._
-A blacklist can be a list of bad e-mail addresses, non-public actions or bad HTML tags. This is opposed to a whitelist which lists the good e-mail addresses, public actions, good HTML tags and so on. Although, sometimes it is not possible to create a whitelist (in a SPAM filter, for example), _(highlight)prefer to use whitelist approaches_:
+A blacklist can be a list of bad e-mail addresses, non-public actions or bad HTML tags. This is opposed to a whitelist which lists the good e-mail addresses, public actions, good HTML tags and so on. Although sometimes it is not possible to create a whitelist (in a SPAM filter, for example), _(highlight)prefer to use whitelist approaches_:
* Use before_filter :only => [...] instead of :except => [...]. This way you don't forget to turn it off for newly added actions.
* Use attr_accessible instead of attr_protected. See the mass-assignment section for details
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index b7c042cee3..01df2c5b64 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,5 +1,7 @@
## Rails 4.0.0 (unreleased) ##
+* The application generator generates `public/humans.txt` with some basic data. *Paul Campbell*
+
* Add `config.queue_consumer` to allow the default consumer to be configurable. *Carlos Antonio da Silva*
* Add Rails.queue as an interface with a default implementation that consumes jobs in a separate thread. *Yehuda Katz*
diff --git a/railties/Rakefile b/railties/Rakefile
index 108413235a..993ba840ff 100644
--- a/railties/Rakefile
+++ b/railties/Rakefile
@@ -20,7 +20,8 @@ namespace :test do
'test',
'lib',
"#{File.dirname(__FILE__)}/../activesupport/lib",
- "#{File.dirname(__FILE__)}/../actionpack/lib"
+ "#{File.dirname(__FILE__)}/../actionpack/lib",
+ "#{File.dirname(__FILE__)}/../activemodel/lib"
]
ruby "-I#{dash_i.join ':'}", file
end
diff --git a/railties/lib/rails/commands.rb b/railties/lib/rails/commands.rb
index 82cdd6053b..7f473c237c 100644
--- a/railties/lib/rails/commands.rb
+++ b/railties/lib/rails/commands.rb
@@ -57,8 +57,7 @@ when 'server'
when 'dbconsole'
require 'rails/commands/dbconsole'
- require APP_PATH
- Rails::DBConsole.start(Rails.application)
+ Rails::DBConsole.start
when 'application', 'runner'
require "rails/commands/#{command}"
diff --git a/railties/lib/rails/commands/dbconsole.rb b/railties/lib/rails/commands/dbconsole.rb
index 25a8caeec3..aaba47117f 100644
--- a/railties/lib/rails/commands/dbconsole.rb
+++ b/railties/lib/rails/commands/dbconsole.rb
@@ -5,15 +5,37 @@ require 'rbconfig'
module Rails
class DBConsole
- attr_reader :arguments
+ attr_reader :arguments, :config
- def self.start(app)
- new(app).start
+ def self.start
+ new(config).start
end
- def initialize(app, arguments = ARGV)
- @app = app
- @arguments = arguments
+ def self.config
+ config = begin
+ YAML.load(ERB.new(IO.read("config/database.yml")).result)
+ rescue SyntaxError, StandardError
+ require APP_PATH
+ Rails.application.config.database_configuration
+ end
+
+ unless config[env]
+ abort "No database is configured for the environment '#{env}'"
+ end
+
+ config[env]
+ end
+
+ def self.env
+ if Rails.respond_to?(:env)
+ Rails.env
+ else
+ ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
+ end
+ end
+
+ def initialize(config, arguments = ARGV)
+ @config, @arguments = config, arguments
end
def start
@@ -38,10 +60,6 @@ module Rails
abort opt.to_s unless (0..1).include?(arguments.size)
end
- unless config = @app.config.database_configuration[Rails.env]
- abort "No database is configured for the environment '#{Rails.env}'"
- end
-
case config["adapter"]
when /^mysql/
diff --git a/railties/lib/rails/configuration.rb b/railties/lib/rails/configuration.rb
index 493eacdc5a..3d66019e5e 100644
--- a/railties/lib/rails/configuration.rb
+++ b/railties/lib/rails/configuration.rb
@@ -1,6 +1,6 @@
require 'active_support/deprecation'
require 'active_support/ordered_options'
-require 'active_support/core_ext/hash/deep_dup'
+require 'active_support/core_ext/object'
require 'rails/paths'
require 'rails/rack'
diff --git a/railties/lib/rails/generators/rails/app/templates/config/application.rb b/railties/lib/rails/generators/rails/app/templates/config/application.rb
index c8a3c13b95..5fa96397f0 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/application.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb
@@ -44,6 +44,9 @@ module <%= app_const_base %>
# Configure sensitive parameters which will be filtered from the log file.
config.filter_parameters += [:password]
+ # Enable escaping HTML in JSON. The default is false.
+ # config.active_support.escape_html_entities_in_json = true
+
# Use SQL instead of Active Record's schema dumper when creating the database.
# This is necessary if your schema can't be completely dumped by the schema dumper,
# like if you have constraints or database-specific column types.
@@ -55,7 +58,7 @@ module <%= app_const_base %>
# parameters by using an attr_accessible or attr_protected declaration.
<%= comment_if :skip_active_record %>config.active_record.whitelist_attributes = true
- # Specifies wether or not has_many or has_one association option :dependent => :restrict raises
+ # Specifies whether or not has_many or has_one association option :dependent => :restrict raises
# an exception. If set to true, then an ActiveRecord::DeleteRestrictionError exception would be
# raised. If set to false, then an error will be added on the model instead.
<%= comment_if :skip_active_record %>config.active_record.dependent_restrict_raises = false
diff --git a/railties/lib/rails/generators/rails/app/templates/public/humans.txt.tt b/railties/lib/rails/generators/rails/app/templates/public/humans.txt.tt
new file mode 100644
index 0000000000..1c49e5c70a
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/public/humans.txt.tt
@@ -0,0 +1,9 @@
+# See more about this file at: http://humanstxt.org/
+# For format suggestions, see: http://humanstxt.org/Standard.html
+/* TEAM */
+ <%= ENV['USER'].titlecase %>
+
+/* APP */
+ Name: <%= app_const_base %>
+ Date Created: <%= Date.today.strftime("%B %d, %Y") %>
+ Software: Ruby on Rails
diff --git a/railties/lib/rails/generators/rails/app/templates/public/robots.txt b/railties/lib/rails/generators/rails/app/templates/public/robots.txt
index 085187fa58..1a3a5e4dd2 100644
--- a/railties/lib/rails/generators/rails/app/templates/public/robots.txt
+++ b/railties/lib/rails/generators/rails/app/templates/public/robots.txt
@@ -1,5 +1,5 @@
# See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file
#
# To ban all spiders from the entire site uncomment the next two lines:
-# User-Agent: *
+# User-agent: *
# Disallow: /
diff --git a/railties/lib/rails/generators/rails/app/templates/test/performance/browsing_test.rb b/railties/lib/rails/generators/rails/app/templates/test/performance/browsing_test.rb
index 3fea27b916..2a849b7f2b 100644
--- a/railties/lib/rails/generators/rails/app/templates/test/performance/browsing_test.rb
+++ b/railties/lib/rails/generators/rails/app/templates/test/performance/browsing_test.rb
@@ -3,7 +3,7 @@ require 'rails/performance_test_help'
class BrowsingTest < ActionDispatch::PerformanceTest
# Refer to the documentation for all available options
- # self.profile_options = { :runs => 5, :metrics => [:wall_time, :memory]
+ # self.profile_options = { :runs => 5, :metrics => [:wall_time, :memory],
# :output => 'tmp/performance', :formats => [:flat] }
def test_homepage
diff --git a/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb b/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb
index f4263d1b98..722e37e20b 100644
--- a/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb
+++ b/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb
@@ -302,7 +302,7 @@ task :default => :test
dummy_application_path = File.expand_path("#{dummy_path}/config/application.rb", destination_root)
unless options[:pretend] || !File.exists?(dummy_application_path)
contents = File.read(dummy_application_path)
- contents[(contents.index("module Dummy"))..-1]
+ contents[(contents.index(/module ([\w]+)\n(.*)class Application/m))..-1]
end
end
end
diff --git a/railties/test/abstract_unit.rb b/railties/test/abstract_unit.rb
index 29ebdc6511..dfcf5aa27d 100644
--- a/railties/test/abstract_unit.rb
+++ b/railties/test/abstract_unit.rb
@@ -1,3 +1,5 @@
+ENV["RAILS_ENV"] ||= "test"
+
require File.expand_path("../../../load_paths", __FILE__)
require 'stringio'
diff --git a/railties/test/commands/dbconsole_test.rb b/railties/test/commands/dbconsole_test.rb
index 0bf417c014..85a7edfacd 100644
--- a/railties/test/commands/dbconsole_test.rb
+++ b/railties/test/commands/dbconsole_test.rb
@@ -3,42 +3,73 @@ require 'rails/commands/dbconsole'
class Rails::DBConsoleTest < ActiveSupport::TestCase
def teardown
- %w[PGUSER PGHOST PGPORT PGPASSWORD'].each{|key| ENV.delete(key)}
+ %w[PGUSER PGHOST PGPORT PGPASSWORD].each{|key| ENV.delete(key)}
end
- def test_no_database_configured
- start [], false
+ def test_config
+ Rails::DBConsole.const_set(:APP_PATH, "erb")
+
+ app_config({})
+ capture_abort { Rails::DBConsole.config }
assert aborted
assert_match /No database is configured for the environment '\w+'/, output
+
+ app_config(test: "with_init")
+ assert_equal Rails::DBConsole.config, "with_init"
+
+ app_db_file("test:\n without_init")
+ assert_equal Rails::DBConsole.config, "without_init"
+
+ app_db_file("test:\n <%= Rails.something_app_specific %>")
+ assert_equal Rails::DBConsole.config, "with_init"
+
+ app_db_file("test:\n\ninvalid")
+ assert_equal Rails::DBConsole.config, "with_init"
+ end
+
+ def test_env
+ assert_equal Rails::DBConsole.env, "test"
+
+ Rails.stubs(:respond_to?).with(:env).returns(false)
+ assert_equal Rails::DBConsole.env, "test"
+
+ ENV['RAILS_ENV'] = nil
+ ENV['RACK_ENV'] = "rack_env"
+ assert_equal Rails::DBConsole.env, "rack_env"
+
+ ENV['RAILS_ENV'] = "rails_env"
+ assert_equal Rails::DBConsole.env, "rails_env"
+ ensure
+ ENV['RAILS_ENV'] = "test"
end
def test_mysql
dbconsole.expects(:find_cmd_and_exec).with(%w[mysql mysql5], 'db')
- start [], {adapter: 'mysql', database: 'db'}
+ start(adapter: 'mysql', database: 'db')
assert !aborted
end
def test_mysql_full
dbconsole.expects(:find_cmd_and_exec).with(%w[mysql mysql5], '--host=locahost', '--port=1234', '--socket=socket', '--user=user', '--default-character-set=UTF-8', '-p', 'db')
- start [], {adapter: 'mysql', database: 'db', host: 'locahost', port: 1234, socket: 'socket', username: 'user', password: 'qwerty', encoding: 'UTF-8'}
+ start(adapter: 'mysql', database: 'db', host: 'locahost', port: 1234, socket: 'socket', username: 'user', password: 'qwerty', encoding: 'UTF-8')
assert !aborted
end
def test_mysql_include_password
dbconsole.expects(:find_cmd_and_exec).with(%w[mysql mysql5], '--user=user', '--password=qwerty', 'db')
- start ['-p'], {adapter: 'mysql', database: 'db', username: 'user', password: 'qwerty'}
+ start({adapter: 'mysql', database: 'db', username: 'user', password: 'qwerty'}, ['-p'])
assert !aborted
end
def test_postgresql
dbconsole.expects(:find_cmd_and_exec).with('psql', 'db')
- start [], {adapter: 'postgresql', database: 'db'}
+ start(adapter: 'postgresql', database: 'db')
assert !aborted
end
def test_postgresql_full
dbconsole.expects(:find_cmd_and_exec).with('psql', 'db')
- start [], {adapter: 'postgresql', database: 'db', username: 'user', password: 'q1w2e3', host: 'host', port: 5432}
+ start(adapter: 'postgresql', database: 'db', username: 'user', password: 'q1w2e3', host: 'host', port: 5432)
assert !aborted
assert_equal 'user', ENV['PGUSER']
assert_equal 'host', ENV['PGHOST']
@@ -48,7 +79,7 @@ class Rails::DBConsoleTest < ActiveSupport::TestCase
def test_postgresql_include_password
dbconsole.expects(:find_cmd_and_exec).with('psql', 'db')
- start ['-p'], {adapter: 'postgresql', database: 'db', username: 'user', password: 'q1w2e3'}
+ start({adapter: 'postgresql', database: 'db', username: 'user', password: 'q1w2e3'}, ['-p'])
assert !aborted
assert_equal 'user', ENV['PGUSER']
assert_equal 'q1w2e3', ENV['PGPASSWORD']
@@ -56,42 +87,42 @@ class Rails::DBConsoleTest < ActiveSupport::TestCase
def test_sqlite
dbconsole.expects(:find_cmd_and_exec).with('sqlite', 'db')
- start [], {adapter: 'sqlite', database: 'db'}
+ start(adapter: 'sqlite', database: 'db')
assert !aborted
end
def test_sqlite3
dbconsole.expects(:find_cmd_and_exec).with('sqlite3', 'db')
- start [], {adapter: 'sqlite3', database: 'db'}
+ start(adapter: 'sqlite3', database: 'db')
assert !aborted
end
def test_sqlite3_mode
dbconsole.expects(:find_cmd_and_exec).with('sqlite3', '-html', 'db')
- start ['--mode', 'html'], {adapter: 'sqlite3', database: 'db'}
+ start({adapter: 'sqlite3', database: 'db'}, ['--mode', 'html'])
assert !aborted
end
def test_sqlite3_header
dbconsole.expects(:find_cmd_and_exec).with('sqlite3', '-header', 'db')
- start ['--header'], {adapter: 'sqlite3', database: 'db'}
+ start({adapter: 'sqlite3', database: 'db'}, ['--header'])
assert !aborted
end
def test_oracle
dbconsole.expects(:find_cmd_and_exec).with('sqlplus', 'user@db')
- start [], {adapter: 'oracle', database: 'db', username: 'user', password: 'secret'}
+ start(adapter: 'oracle', database: 'db', username: 'user', password: 'secret')
assert !aborted
end
def test_oracle_include_password
dbconsole.expects(:find_cmd_and_exec).with('sqlplus', 'user/secret@db')
- start ['-p'], {adapter: 'oracle', database: 'db', username: 'user', password: 'secret'}
+ start({adapter: 'oracle', database: 'db', username: 'user', password: 'secret'}, ['-p'])
assert !aborted
end
def test_unknown_command_line_client
- start [], {adapter: 'unknown', database: 'db'}
+ start(adapter: 'unknown', database: 'db')
assert aborted
assert_match /Unknown command-line client for db/, output
end
@@ -100,29 +131,30 @@ class Rails::DBConsoleTest < ActiveSupport::TestCase
attr_reader :aborted, :output
def dbconsole
- @dbconsole ||= Rails::DBConsole.new(app)
+ @dbconsole ||= Rails::DBConsole.new(nil)
end
- def start(argv = [], database_configuration = {})
- dbconsole.stubs(arguments: argv)
- app.config.stubs(database_configuration: {
- Rails.env => database_configuration ? database_configuration.stringify_keys : database_configuration
- })
+ def start(config = {}, argv = [])
+ dbconsole.stubs(config: config.stringify_keys, arguments: argv)
+ capture_abort { dbconsole.start }
+ end
+ def capture_abort
@aborted = false
- @output = capture(:stderr) do
+ @output = capture(:stderr) do
begin
- dbconsole.start
+ yield
rescue SystemExit
@aborted = true
end
end
end
- def app
- @app ||= begin
- config = mock("config")
- stub("app", config: config)
- end
+ def app_db_file(result)
+ IO.stubs(:read).with("config/database.yml").returns(result)
+ end
+
+ def app_config(result)
+ Rails.application.config.stubs(:database_configuration).returns(result.stringify_keys)
end
end
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index d13dc8d4ac..a0c308a3b8 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -383,6 +383,12 @@ class AppGeneratorTest < Rails::Generators::TestCase
assert_no_match(/run bundle install/, output)
end
+ def test_humans_txt_file
+ date = Date.today.strftime("%B %d, %Y")
+ run_generator [File.join(destination_root, 'things-43')]
+ assert_file "things-43/public/humans.txt", /Name: Things43/, /Software: Ruby on Rails/, /Date Created: #{date}/
+ end
+
protected
def action(*args, &block)
diff --git a/railties/test/generators/plugin_new_generator_test.rb b/railties/test/generators/plugin_new_generator_test.rb
index 6c31b80c7d..51374e5f3f 100644
--- a/railties/test/generators/plugin_new_generator_test.rb
+++ b/railties/test/generators/plugin_new_generator_test.rb
@@ -236,6 +236,13 @@ class PluginNewGeneratorTest < Rails::Generators::TestCase
assert_no_file "test/dummy"
end
+ def test_creating_dummy_application_with_different_name
+ run_generator [destination_root, "--dummy_path", "spec/fake"]
+ assert_file "spec/fake"
+ assert_file "spec/fake/config/application.rb"
+ assert_no_file "test/dummy"
+ end
+
def test_creating_dummy_without_tests_but_with_dummy_path
run_generator [destination_root, "--dummy_path", "spec/dummy", "--skip-test-unit"]
assert_file "spec/dummy"
diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb
index f2dc0c71b1..03be81e59f 100644
--- a/railties/test/isolation/abstract_unit.rb
+++ b/railties/test/isolation/abstract_unit.rb
@@ -17,8 +17,8 @@ RAILS_FRAMEWORK_ROOT = File.expand_path("#{File.dirname(__FILE__)}/../../..")
# These files do not require any others and are needed
# to run the tests
-require "#{RAILS_FRAMEWORK_ROOT}/activesupport/lib/active_support/testing/isolation"
-require "#{RAILS_FRAMEWORK_ROOT}/activesupport/lib/active_support/core_ext/kernel/reporting"
+require "active_support/testing/isolation"
+require "active_support/core_ext/kernel/reporting"
module TestHelpers
module Paths
@@ -248,7 +248,6 @@ module TestHelpers
def use_frameworks(arr)
to_remove = [:actionmailer,
- :activemodel,
:activerecord] - arr
if to_remove.include? :activerecord
remove_from_config "config.active_record.whitelist_attributes = true"