aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Gemfile5
-rw-r--r--Gemfile.lock39
-rw-r--r--actionmailer/CHANGELOG.md11
-rw-r--r--actionmailer/lib/action_mailer/base.rb5
-rw-r--r--actionmailer/test/mail_helper_test.rb14
-rw-r--r--actionpack/CHANGELOG.md15
-rw-r--r--actionpack/lib/action_controller/metal/request_forgery_protection.rb11
-rw-r--r--actionpack/lib/action_dispatch/journey/route.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/cookies.rb21
-rw-r--r--actionpack/lib/action_dispatch/middleware/static.rb19
-rw-r--r--actionpack/lib/action_dispatch/routing/inspector.rb2
-rw-r--r--actionpack/test/controller/redirect_test.rb3
-rw-r--r--actionpack/test/controller/request_forgery_protection_test.rb17
-rw-r--r--actionpack/test/controller/routing_test.rb8
-rw-r--r--actionpack/test/dispatch/routing/inspector_test.rb16
-rw-r--r--actionpack/test/journey/route_test.rb8
-rw-r--r--actionview/CHANGELOG.md7
-rw-r--r--actionview/lib/action_view/helpers/cache_helper.rb2
-rw-r--r--actionview/lib/action_view/helpers/translation_helper.rb59
-rw-r--r--actionview/test/activerecord/form_helper_activerecord_test.rb4
-rw-r--r--actionview/test/template/capture_helper_test.rb6
-rw-r--r--actionview/test/template/date_helper_test.rb10
-rw-r--r--actionview/test/template/url_helper_test.rb5
-rw-r--r--activejob/CHANGELOG.md6
-rw-r--r--activejob/lib/active_job/arguments.rb7
-rw-r--r--activejob/lib/active_job/core.rb2
-rw-r--r--activejob/lib/active_job/enqueuing.rb1
-rw-r--r--activejob/lib/active_job/queue_adapters.rb1
-rw-r--r--activejob/lib/active_job/queue_adapters/inline_adapter.rb2
-rw-r--r--activejob/lib/active_job/queue_adapters/qu_adapter.rb2
-rw-r--r--activejob/lib/active_job/queue_adapters/sneakers_adapter.rb2
-rw-r--r--activejob/lib/active_job/queue_adapters/sucker_punch_adapter.rb2
-rw-r--r--activejob/lib/active_job/test_helper.rb4
-rw-r--r--activejob/lib/rails/generators/job/job_generator.rb2
-rw-r--r--activejob/test/cases/test_helper_test.rb22
-rw-r--r--activejob/test/integration/queuing_test.rb2
-rw-r--r--activemodel/CHANGELOG.md11
-rw-r--r--activemodel/lib/active_model/attribute_methods.rb14
-rw-r--r--activemodel/lib/active_model/dirty.rb23
-rw-r--r--activemodel/lib/active_model/validations.rb1
-rw-r--r--activemodel/test/cases/dirty_test.rb13
-rw-r--r--activerecord/CHANGELOG.md34
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb17
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql_adapter.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb3
-rw-r--r--activerecord/lib/active_record/enum.rb2
-rw-r--r--activerecord/lib/active_record/nested_attributes.rb4
-rw-r--r--activerecord/lib/active_record/persistence.rb6
-rw-r--r--activerecord/lib/active_record/railties/databases.rake2
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb8
-rw-r--r--activerecord/lib/active_record/relation/where_clause.rb2
-rw-r--r--activerecord/lib/rails/generators/active_record/migration/migration_generator.rb9
-rw-r--r--activerecord/test/cases/disconnected_test.rb4
-rw-r--r--activerecord/test/cases/migration/references_foreign_key_test.rb20
-rw-r--r--activerecord/test/cases/relations_test.rb8
-rw-r--r--activerecord/test/cases/scoping/default_scoping_test.rb12
-rw-r--r--activerecord/test/cases/timestamp_test.rb11
-rw-r--r--activesupport/CHANGELOG.md14
-rw-r--r--activesupport/lib/active_support/core_ext/module/aliasing.rb3
-rw-r--r--activesupport/lib/active_support/deprecation/proxy_wrappers.rb60
-rw-r--r--activesupport/lib/active_support/rails.rb4
-rw-r--r--activesupport/lib/active_support/rescuable.rb2
-rw-r--r--activesupport/lib/active_support/subscriber.rb4
-rw-r--r--activesupport/lib/active_support/testing/isolation.rb8
-rw-r--r--activesupport/lib/active_support/time_with_zone.rb13
-rw-r--r--activesupport/lib/active_support/values/time_zone.rb72
-rw-r--r--activesupport/test/core_ext/time_with_zone_test.rb47
-rw-r--r--activesupport/test/time_zone_test.rb83
-rw-r--r--guides/source/4_2_release_notes.md5
-rw-r--r--guides/source/action_mailer_basics.md3
-rw-r--r--guides/source/action_view_overview.md45
-rw-r--r--guides/source/active_record_querying.md4
-rw-r--r--guides/source/api_documentation_guidelines.md3
-rw-r--r--guides/source/caching_with_rails.md22
-rw-r--r--guides/source/configuring.md4
-rw-r--r--guides/source/contributing_to_ruby_on_rails.md33
-rw-r--r--guides/source/getting_started.md7
-rw-r--r--guides/source/ruby_on_rails_guides_guidelines.md6
-rw-r--r--guides/source/testing.md46
-rw-r--r--railties/CHANGELOG.md24
-rw-r--r--railties/lib/rails/application.rb2
-rw-r--r--railties/lib/rails/commands/dbconsole.rb5
-rw-r--r--railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb2
-rw-r--r--railties/lib/rails/generators/rails/controller/controller_generator.rb3
-rw-r--r--railties/lib/rails/generators/rails/plugin/plugin_generator.rb2
-rw-r--r--railties/lib/rails/generators/rails/scaffold/templates/scaffold.css27
-rw-r--r--railties/lib/rails/mailers_controller.rb10
-rw-r--r--railties/lib/rails/rack/logger.rb3
-rw-r--r--railties/lib/rails/templates/rails/mailers/email.html.erb13
-rw-r--r--railties/lib/rails/templates/rails/welcome/index.html.erb13
-rw-r--r--railties/test/application/assets_test.rb12
-rw-r--r--railties/test/application/mailer_previews_test.rb26
-rw-r--r--railties/test/application/rake/dbs_test.rb28
-rw-r--r--railties/test/commands/dbconsole_test.rb6
-rw-r--r--railties/test/generators/named_base_test.rb23
-rw-r--r--railties/test/generators_test.rb6
-rw-r--r--railties/test/path_generation_test.rb6
99 files changed, 916 insertions, 340 deletions
diff --git a/Gemfile b/Gemfile
index ddaaacdc59..78224f60ed 100644
--- a/Gemfile
+++ b/Gemfile
@@ -18,6 +18,7 @@ gem 'arel', github: 'rails/arel', branch: 'master'
gem 'mail', github: 'mikel/mail'
gem 'sprockets', '~> 3.0.0.rc.1'
+gem 'sprockets-rails', github: 'rails/sprockets-rails', branch: 'master'
# require: false so bcrypt is loaded only when has_secure_password is used.
# This is to avoid ActiveModel (and by extension the entire framework)
@@ -46,7 +47,7 @@ group :job do
gem 'sucker_punch', require: false
gem 'delayed_job', require: false
gem 'queue_classic', require: false, platforms: :ruby
- gem 'sneakers', '0.1.1.pre', require: false
+ gem 'sneakers', require: false
gem 'que', require: false
gem 'backburner', require: false
gem 'qu-rails', github: "bkeepers/qu", branch: "master", require: false
@@ -65,7 +66,7 @@ group :test do
platforms :mri do
gem 'stackprof'
- # gem 'byebug'
+ gem 'byebug'
end
gem 'benchmark-ips'
diff --git a/Gemfile.lock b/Gemfile.lock
index 543cfaf3da..5adbf65045 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -35,6 +35,16 @@ GIT
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
+GIT
+ remote: git://github.com/rails/sprockets-rails.git
+ revision: 85b89c44ad40af3056899808475e6e4bf65c1f5a
+ branch: master
+ specs:
+ sprockets-rails (3.0.0.beta1)
+ actionpack (>= 4.0)
+ activesupport (>= 4.0)
+ sprockets (>= 3.0.0, < 4.0)
+
PATH
remote: .
specs:
@@ -104,8 +114,10 @@ GEM
beaneater (0.3.3)
benchmark-ips (2.1.1)
builder (3.2.2)
- bunny (1.1.9)
+ bunny (1.7.0)
amq-protocol (>= 1.9.2)
+ byebug (4.0.5)
+ columnize (= 0.9.0)
celluloid (0.16.0)
timers (~> 4.0.0)
coffee-rails (4.1.0)
@@ -115,6 +127,7 @@ GEM
coffee-script-source
execjs
coffee-script-source (1.9.0)
+ columnize (0.9.0)
connection_pool (2.1.1)
dalli (2.7.2)
dante (0.1.5)
@@ -169,7 +182,7 @@ GEM
rack (>= 1.0)
rails-deprecated_sanitizer (1.0.3)
activesupport (>= 4.2.0.alpha)
- rails-dom-testing (1.0.5)
+ rails-dom-testing (1.0.6)
activesupport (>= 4.2.0.beta, < 5.0)
nokogiri (~> 1.6.0)
rails-deprecated_sanitizer (>= 1.0.1)
@@ -211,24 +224,20 @@ GEM
rack (~> 1.4)
rack-protection (~> 1.4)
tilt (~> 1.3, >= 1.3.4)
- sneakers (0.1.1.pre)
- bunny (~> 1.1.3)
- serverengine
+ sneakers (1.0.4)
+ bunny (~> 1.7.0)
+ serverengine (~> 1.5.5)
thor
- thread
- sprockets (3.0.0.rc.1)
+ thread (~> 0.1.7)
+ sprockets (3.0.2)
rack (~> 1.0)
- sprockets-rails (2.2.4)
- actionpack (>= 3.0)
- activesupport (>= 3.0)
- sprockets (>= 2.8, < 4.0)
sqlite3 (1.3.10)
stackprof (0.2.7)
sucker_punch (1.3.2)
celluloid (~> 0.16.0)
thor (0.19.1)
- thread (0.1.5)
- thread_safe (0.3.4)
+ thread (0.1.7)
+ thread_safe (0.3.5)
tilt (1.4.1)
timers (4.0.1)
hitimes
@@ -258,6 +267,7 @@ DEPENDENCIES
backburner
bcrypt (~> 3.1.10)
benchmark-ips
+ byebug
coffee-rails (~> 4.1.0)
dalli (>= 2.2.1)
delayed_job
@@ -287,8 +297,9 @@ DEPENDENCIES
sdoc (~> 0.4.0)
sequel
sidekiq
- sneakers (= 0.1.1.pre)
+ sneakers
sprockets (~> 3.0.0.rc.1)
+ sprockets-rails!
sqlite3 (~> 1.3.6)
stackprof
sucker_punch
diff --git a/actionmailer/CHANGELOG.md b/actionmailer/CHANGELOG.md
index 86ecb3ee88..0d47ce855a 100644
--- a/actionmailer/CHANGELOG.md
+++ b/actionmailer/CHANGELOG.md
@@ -1,3 +1,14 @@
+* Mailer previews no longer crash when the `mail` method wasn't called
+ (`NullMail`).
+
+ Fixes #19849.
+
+ *Yves Senn*
+
+* Make sure labels and values line up in mailer previews.
+
+ *Yves Senn*
+
* Add `assert_enqueued_emails` and `assert_no_enqueued_emails`.
Example:
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb
index 6ddc4c9596..218b7a735a 100644
--- a/actionmailer/lib/action_mailer/base.rb
+++ b/actionmailer/lib/action_mailer/base.rb
@@ -379,8 +379,8 @@ module ActionMailer
# * <tt>:password</tt> - If your mail server requires authentication, set the password in this setting.
# * <tt>:authentication</tt> - If your mail server requires authentication, you need to specify the
# authentication type here.
- # This is a symbol and one of <tt>:plain</tt> (will send the password in the clear), <tt>:login</tt> (will
- # send password Base64 encoded) or <tt>:cram_md5</tt> (combines a Challenge/Response mechanism to exchange
+ # This is a symbol and one of <tt>:plain</tt> (will send the password Base64 encoded), <tt>:login</tt> (will
+ # send the password Base64 encoded) or <tt>:cram_md5</tt> (combines a Challenge/Response mechanism to exchange
# information and a cryptographic Message Digest 5 algorithm to hash important information)
# * <tt>:enable_starttls_auto</tt> - Detects if STARTTLS is enabled in your SMTP server and starts
# to use it. Defaults to <tt>true</tt>.
@@ -596,6 +596,7 @@ module ActionMailer
class NullMail #:nodoc:
def body; '' end
+ def header; {} end
def respond_to?(string, include_all=false)
true
diff --git a/actionmailer/test/mail_helper_test.rb b/actionmailer/test/mail_helper_test.rb
index 24ccaab8df..ff6b25b0c7 100644
--- a/actionmailer/test/mail_helper_test.rb
+++ b/actionmailer/test/mail_helper_test.rb
@@ -59,6 +59,12 @@ The second
end
end
+ def use_cache
+ mail_with_defaults do |format|
+ format.html { render(inline: "<% cache(:foo) do %>Greetings from a cache helper block<% end %>") }
+ end
+ end
+
protected
def mail_with_defaults(&block)
@@ -107,5 +113,11 @@ class MailerHelperTest < ActionMailer::TestCase
TEXT
assert_equal expected.gsub("\n", "\r\n"), mail.body.encoded
end
-end
+ def test_use_cache
+ assert_nothing_raised do
+ mail = HelperMailer.use_cache
+ assert_equal "Greetings from a cache helper block", mail.body.encoded
+ end
+ end
+end
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index a5e551c78e..e0076225ba 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,3 +1,10 @@
+* Fix rake routes not showing the right format when
+ nesting multiple routes.
+
+ See #18373.
+
+ *Ravil Bayramgalin*
+
* Add ability to override default form builder for a controller.
class AdminController < ApplicationController
@@ -35,11 +42,11 @@
*Kohei Suzuki*
-* Fix handling of empty X_FORWARDED_HOST header in raw_host_with_port
+* Fix handling of empty `X_FORWARDED_HOST` header in `raw_host_with_port`.
- Previously, an empty X_FORWARDED_HOST header would cause
- Actiondispatch::Http:URL.raw_host_with_port to return nil, causing
- Actiondispatch::Http:URL.host to raise a NoMethodError.
+ Previously, an empty `X_FORWARDED_HOST` header would cause
+ `Actiondispatch::Http:URL.raw_host_with_port` to return `nil`, causing
+ `Actiondispatch::Http:URL.host` to raise a `NoMethodError`.
*Adam Forsyth*
diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
index 663a969f72..31c8856437 100644
--- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb
+++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
@@ -15,9 +15,9 @@ module ActionController #:nodoc:
# access. When a request reaches your application, \Rails verifies the received
# token with the token in the session. All requests are checked except GET requests
# as these should be idempotent. Keep in mind that all session-oriented requests
- # should be CSRF protected, including Javascript and HTML requests.
+ # should be CSRF protected, including JavaScript and HTML requests.
#
- # Since HTML and Javascript requests are typically made from the browser, we
+ # Since HTML and JavaScript requests are typically made from the browser, we
# need to ensure to verify request authenticity for the web browser. We can
# use session-oriented authentication for these types requests, by using
# the `protect_form_forgery` method in our controllers.
@@ -40,7 +40,8 @@ module ActionController #:nodoc:
#
# CSRF protection is turned on with the <tt>protect_from_forgery</tt> method.
# By default <tt>protect_from_forgery</tt> protects your session with
- # <tt>:null_session</tt> method, which provides an empty session during request
+ # <tt>:null_session</tt> method, which provides an empty session
+ # during request.
#
# We may want to disable CSRF protection for APIs since they are typically
# designed to be state-less. That is, the requestion API client will handle
@@ -96,10 +97,10 @@ module ActionController #:nodoc:
# Valid Options:
#
# * <tt>:only/:except</tt> - Only apply forgery protection to a subset of actions. Like <tt>only: [ :create, :create_all ]</tt>.
- # * <tt>:if/:unless</tt> - Turn off the forgery protection entirely depending on the passed proc or method reference.
+ # * <tt>:if/:unless</tt> - Turn off the forgery protection entirely depending on the passed Proc or method reference.
# * <tt>:prepend</tt> - By default, the verification of the authentication token is added to the front of the
# callback chain. If you need to make the verification depend on other callbacks, like authentication methods
- # (say cookies vs oauth), this might not work for you. Pass <tt>prepend: false</tt> to just add the
+ # (say cookies vs OAuth), this might not work for you. Pass <tt>prepend: false</tt> to just add the
# verification callback in the position of the protect_from_forgery call. This means any callbacks added
# before are run first.
# * <tt>:with</tt> - Set the method to handle unverified request.
diff --git a/actionpack/lib/action_dispatch/journey/route.rb b/actionpack/lib/action_dispatch/journey/route.rb
index 4d5c18984a..4698ff8cc7 100644
--- a/actionpack/lib/action_dispatch/journey/route.rb
+++ b/actionpack/lib/action_dispatch/journey/route.rb
@@ -36,7 +36,7 @@ module ActionDispatch
def requirements # :nodoc:
# needed for rails `rake routes`
- path.requirements.merge(@defaults).delete_if { |_,v|
+ @defaults.merge(path.requirements).delete_if { |_,v|
/.+?/ == v
}
end
diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb
index b7687ca100..dd1f140051 100644
--- a/actionpack/lib/action_dispatch/middleware/cookies.rb
+++ b/actionpack/lib/action_dispatch/middleware/cookies.rb
@@ -79,6 +79,9 @@ module ActionDispatch
# domain: %w(.example.com .example.org) # Allow the cookie
# # for concrete domain names.
#
+ # * <tt>:tld_length</tt> - When using <tt>:domain => :all</tt>, this option can be used to explicitly
+ # set the TLD length when using a short (<= 3 character) domain that is being interpreted as part of a TLD.
+ # For example, to share cookies between user1.lvh.me and user2.lvh.me, set <tt>:tld_length</tt> to 1.
# * <tt>:expires</tt> - The time at which this cookie expires, as a \Time object.
# * <tt>:secure</tt> - Whether this cookie is only transmitted to HTTPS servers.
# Default is +false+.
@@ -181,7 +184,7 @@ module ActionDispatch
# to the Message{Encryptor,Verifier} allows us to handle the
# (de)serialization step within the cookie jar, which gives us the
# opportunity to detect and migrate legacy cookies.
- module VerifyAndUpgradeLegacySignedMessage
+ module VerifyAndUpgradeLegacySignedMessage # :nodoc:
def initialize(*args)
super
@legacy_verifier = ActiveSupport::MessageVerifier.new(@options[:secret_token], serializer: ActiveSupport::MessageEncryptor::NullSerializer)
@@ -392,7 +395,7 @@ module ActionDispatch
end
end
- class JsonSerializer
+ class JsonSerializer # :nodoc:
def self.load(value)
ActiveSupport::JSON.decode(value)
end
@@ -402,7 +405,7 @@ module ActionDispatch
end
end
- module SerializedCookieJars
+ module SerializedCookieJars # :nodoc:
MARSHAL_SIGNATURE = "\x04\x08".freeze
protected
@@ -454,12 +457,16 @@ module ActionDispatch
@verifier = ActiveSupport::MessageVerifier.new(secret, digest: digest, serializer: ActiveSupport::MessageEncryptor::NullSerializer)
end
+ # Returns the value of the cookie by +name+ if it is untampered,
+ # returns +nil+ otherwise or if no such cookie exists.
def [](name)
if signed_message = @parent_jar[name]
deserialize name, verify(signed_message)
end
end
+ # Signs and sets the cookie named +name+. The second argument may be the cookie's
+ # value or a hash of options as documented above.
def []=(name, options)
if options.is_a?(Hash)
options.symbolize_keys!
@@ -482,8 +489,8 @@ module ActionDispatch
# UpgradeLegacySignedCookieJar is used instead of SignedCookieJar if
# secrets.secret_token and secrets.secret_key_base are both set. It reads
- # legacy cookies signed with the old dummy key generator and re-saves
- # them using the new key generator to provide a smooth upgrade path.
+ # legacy cookies signed with the old dummy key generator and signs and
+ # re-saves them using the new key generator to provide a smooth upgrade path.
class UpgradeLegacySignedCookieJar < SignedCookieJar #:nodoc:
include VerifyAndUpgradeLegacySignedMessage
@@ -511,12 +518,16 @@ module ActionDispatch
@encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret, digest: digest, serializer: ActiveSupport::MessageEncryptor::NullSerializer)
end
+ # Returns the value of the cookie by +name+ if it is untampered,
+ # returns +nil+ otherwise or if no such cookie exists.
def [](name)
if encrypted_message = @parent_jar[name]
deserialize name, decrypt_and_verify(encrypted_message)
end
end
+ # Encrypts and sets the cookie named +name+. The second argument may be the cookie's
+ # value or a hash of options as documented above.
def []=(name, options)
if options.is_a?(Hash)
options.symbolize_keys!
diff --git a/actionpack/lib/action_dispatch/middleware/static.rb b/actionpack/lib/action_dispatch/middleware/static.rb
index 9a92b690c7..c47e5d5245 100644
--- a/actionpack/lib/action_dispatch/middleware/static.rb
+++ b/actionpack/lib/action_dispatch/middleware/static.rb
@@ -3,15 +3,15 @@ require 'active_support/core_ext/uri'
module ActionDispatch
# This middleware returns a file's contents from disk in the body response.
- # When initialized it can accept an optional 'Cache-Control' header which
+ # When initialized, it can accept an optional 'Cache-Control' header, which
# will be set when a response containing a file's contents is delivered.
#
# This middleware will render the file specified in `env["PATH_INFO"]`
- # where the base path is in the +root+ directory. For example if the +root+
- # is set to `public/` then a request with `env["PATH_INFO"]` of
- # `assets/application.js` will return a response with contents of a file
+ # where the base path is in the +root+ directory. For example, if the +root+
+ # is set to `public/`, then a request with `env["PATH_INFO"]` of
+ # `assets/application.js` will return a response with the contents of a file
# located at `public/assets/application.js` if the file exists. If the file
- # does not exist a 404 "File not Found" response will be returned.
+ # does not exist, a 404 "File not Found" response will be returned.
class FileHandler
def initialize(root, cache_control)
@root = root.chomp('/')
@@ -20,6 +20,13 @@ module ActionDispatch
@file_server = ::Rack::File.new(@root, headers)
end
+
+ # Takes a path to a file. If the file is found, has valid encoding, and has
+ # correct read permissions, the return value is a URI-escaped string
+ # representing the filename. Otherwise, false is returned.
+ #
+ # Used by the `Static` class to check the existence of a valid file
+ # in the server's `public/` directory. (See Static#call)
def match?(path)
path = URI.parser.unescape(path)
return false unless path.valid_encoding?
@@ -88,7 +95,7 @@ module ActionDispatch
end
# This middleware will attempt to return the contents of a file's body from
- # disk in the response. If a file is not found on disk, the request will be
+ # disk in the response. If a file is not found on disk, the request will be
# delegated to the application stack. This middleware is commonly initialized
# to serve assets from a server's `public/` directory.
#
diff --git a/actionpack/lib/action_dispatch/routing/inspector.rb b/actionpack/lib/action_dispatch/routing/inspector.rb
index c513737fc2..48c10a7d4c 100644
--- a/actionpack/lib/action_dispatch/routing/inspector.rb
+++ b/actionpack/lib/action_dispatch/routing/inspector.rb
@@ -45,7 +45,7 @@ module ActionDispatch
end
def internal?
- controller.to_s =~ %r{\Arails/(info|mailers|welcome)} || path =~ %r{\A#{Rails.application.config.assets.prefix}\z}
+ controller.to_s =~ %r{\Arails/(info|mailers|welcome)}
end
def engine?
diff --git a/actionpack/test/controller/redirect_test.rb b/actionpack/test/controller/redirect_test.rb
index efd790de63..ef30f1ea0f 100644
--- a/actionpack/test/controller/redirect_test.rb
+++ b/actionpack/test/controller/redirect_test.rb
@@ -1,8 +1,5 @@
require 'abstract_unit'
-class WorkshopsController < ActionController::Base
-end
-
class RedirectController < ActionController::Base
# empty method not used anywhere to ensure methods like
# `status` and `location` aren't called on `redirect_to` calls
diff --git a/actionpack/test/controller/request_forgery_protection_test.rb b/actionpack/test/controller/request_forgery_protection_test.rb
index 8887f291cf..f8cf79a257 100644
--- a/actionpack/test/controller/request_forgery_protection_test.rb
+++ b/actionpack/test/controller/request_forgery_protection_test.rb
@@ -12,14 +12,6 @@ module RequestForgeryProtectionActions
render :inline => "<%= button_to('New', '/') %>"
end
- def external_form
- render :inline => "<%= form_tag('http://farfar.away/form', :authenticity_token => 'external_token') {} %>"
- end
-
- def external_form_without_protection
- render :inline => "<%= form_tag('http://farfar.away/form', :authenticity_token => false) {} %>"
- end
-
def unsafe
render :text => 'pwn'
end
@@ -28,14 +20,6 @@ module RequestForgeryProtectionActions
render :inline => "<%= csrf_meta_tags %>"
end
- def external_form_for
- render :inline => "<%= form_for(:some_resource, :authenticity_token => 'external_token') {} %>"
- end
-
- def form_for_without_protection
- render :inline => "<%= form_for(:some_resource, :authenticity_token => false ) {} %>"
- end
-
def form_for_remote
render :inline => "<%= form_for(:some_resource, :remote => true ) {} %>"
end
@@ -70,7 +54,6 @@ module RequestForgeryProtectionActions
negotiate_same_origin
end
- def rescue_action(e) raise e end
end
# sample controllers
diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb
index 2d08987ca6..9bbfb74e72 100644
--- a/actionpack/test/controller/routing_test.rb
+++ b/actionpack/test/controller/routing_test.rb
@@ -8,8 +8,6 @@ class MilestonesController < ActionController::Base
alias_method :show, :index
end
-ROUTING = ActionDispatch::Routing
-
# See RFC 3986, section 3.3 for allowed path characters.
class UriReservedCharactersRoutingTest < ActiveSupport::TestCase
include RoutingTestHelpers
@@ -871,7 +869,7 @@ class RouteSetTest < ActiveSupport::TestCase
def default_route_set
@default_route_set ||= begin
- set = ROUTING::RouteSet.new
+ set = ActionDispatch::Routing::RouteSet.new
set.draw do
get '/:controller(/:action(/:id))'
end
@@ -1748,13 +1746,13 @@ class RouteSetTest < ActiveSupport::TestCase
include ActionDispatch::RoutingVerbs
- class TestSet < ROUTING::RouteSet
+ class TestSet < ActionDispatch::Routing::RouteSet
def initialize(block)
@block = block
super()
end
- class Dispatcher < ROUTING::RouteSet::Dispatcher
+ class Dispatcher < ActionDispatch::Routing::RouteSet::Dispatcher
def initialize(defaults, set, block)
super(defaults)
@block = block
diff --git a/actionpack/test/dispatch/routing/inspector_test.rb b/actionpack/test/dispatch/routing/inspector_test.rb
index 3df022c64b..4047214843 100644
--- a/actionpack/test/dispatch/routing/inspector_test.rb
+++ b/actionpack/test/dispatch/routing/inspector_test.rb
@@ -313,6 +313,22 @@ module ActionDispatch
assert_equal ["Prefix Verb URI Pattern Controller#Action",
" GET /:controller(/:action) (?-mix:api\\/[^\\/]+)#:action"], output
end
+
+ def test_inspect_routes_shows_resources_route_when_assets_disabled
+ @set = ActionDispatch::Routing::RouteSet.new
+ app = ActiveSupport::OrderedOptions.new
+
+ Rails.stubs(:application).returns(app)
+
+ output = draw do
+ get '/cart', to: 'cart#show'
+ end
+
+ assert_equal [
+ "Prefix Verb URI Pattern Controller#Action",
+ " cart GET /cart(.:format) cart#show"
+ ], output
+ end
end
end
end
diff --git a/actionpack/test/journey/route_test.rb b/actionpack/test/journey/route_test.rb
index 21d867aca0..9616f036b3 100644
--- a/actionpack/test/journey/route_test.rb
+++ b/actionpack/test/journey/route_test.rb
@@ -25,6 +25,14 @@ module ActionDispatch
end
end
+ def test_path_requirements_override_defaults
+ strexp = Router::Strexp.build(':name', { name: /love/ }, ['/'])
+ path = Path::Pattern.new strexp
+ defaults = { name: 'tender' }
+ route = Route.new('name', nil, path, nil, defaults)
+ assert_equal /love/, route.requirements[:name]
+ end
+
def test_ip_address
path = Path::Pattern.from_string '/messages/:id(.:format)'
route = Route.new("name", nil, path, {:ip => '192.168.1.1'},
diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md
index 74dbff80da..506fbbe129 100644
--- a/actionview/CHANGELOG.md
+++ b/actionview/CHANGELOG.md
@@ -1,3 +1,7 @@
+* `link_to_if` passes the block along.
+
+ *Steven Spiel*
+
* Load the `default_form_builder` from the controller on initialization, which overrides
the global config if it is present.
@@ -14,8 +18,7 @@
*Adam Prescott*
* `translate` should accept nils as members of the `:default`
- parameter without raising a translation missing error. Fixes a
- regression introduced 362557e.
+ parameter without raising a translation missing error.
Fixes #19419
diff --git a/actionview/lib/action_view/helpers/cache_helper.rb b/actionview/lib/action_view/helpers/cache_helper.rb
index 0e2a5f90f4..4fe21ca05b 100644
--- a/actionview/lib/action_view/helpers/cache_helper.rb
+++ b/actionview/lib/action_view/helpers/cache_helper.rb
@@ -134,7 +134,7 @@ module ActionView
#
# <%= render @notifications, cache: false %>
def cache(name = {}, options = nil, &block)
- if controller.perform_caching
+ if controller.respond_to?(:perform_caching) && controller.perform_caching
safe_concat(fragment_for(cache_fragment_name(name, options), options, &block))
else
yield
diff --git a/actionview/lib/action_view/helpers/translation_helper.rb b/actionview/lib/action_view/helpers/translation_helper.rb
index 9d7390f1fd..089627a221 100644
--- a/actionview/lib/action_view/helpers/translation_helper.rb
+++ b/actionview/lib/action_view/helpers/translation_helper.rb
@@ -7,34 +7,47 @@ module ActionView
module Helpers
module TranslationHelper
include TagHelper
- # Delegates to <tt>I18n#translate</tt> but also performs three additional functions.
+ # Delegates to <tt>I18n#translate</tt> but also performs three additional
+ # functions.
#
- # First, it will ensure that any thrown +MissingTranslation+ messages will be turned
- # into inline spans that:
+ # First, it will ensure that any thrown +MissingTranslation+ messages will
+ # be rendered as inline spans that:
#
- # * have a "translation-missing" class set,
- # * contain the missing key as a title attribute and
- # * a titleized version of the last key segment as a text.
+ # * Have a <tt>translation-missing</tt> class applied
+ # * Contain the missing key as the value of the +title+ attribute
+ # * Have a titleized version of the last key segment as text
#
- # E.g. the value returned for a missing translation key :"blog.post.title" will be
- # <span class="translation_missing" title="translation missing: en.blog.post.title">Title</span>.
- # This way your views will display rather reasonable strings but it will still
- # be easy to spot missing translations.
+ # For example, the value returned for the missing translation key
+ # <tt>"blog.post.title"</tt> will be:
#
- # Second, it'll scope the key by the current partial if the key starts
- # with a period. So if you call <tt>translate(".foo")</tt> from the
- # <tt>people/index.html.erb</tt> template, you'll actually be calling
- # <tt>I18n.translate("people.index.foo")</tt>. This makes it less repetitive
- # to translate many keys within the same partials and gives you a simple framework
- # for scoping them consistently. If you don't prepend the key with a period,
- # nothing is converted.
+ # <span
+ # class="translation_missing"
+ # title="translation missing: en.blog.post.title">Title</span>
#
- # Third, it'll mark the translation as safe HTML if the key has the suffix
- # "_html" or the last element of the key is the word "html". For example,
- # calling translate("footer_html") or translate("footer.html") will return
- # a safe HTML string that won't be escaped by other HTML helper methods. This
- # naming convention helps to identify translations that include HTML tags so that
- # you know what kind of output to expect when you call translate in a template.
+ # This allows for views to display rather reasonable strings while still
+ # giving developers a way to find missing translations.
+ #
+ # If you would prefer missing translations to raise an error, you can
+ # opt out of span-wrapping behavior globally by setting
+ # <tt>ActionView::Base.raise_on_missing_translations = true</tt> or
+ # individually by passing <tt>raise: true</tt> as an option to
+ # <tt>translate</tt>.
+ #
+ # Second, if the key starts with a period <tt>translate</tt> will scope
+ # the key by the current partial. Calling <tt>translate(".foo")</tt> from
+ # the <tt>people/index.html.erb</tt> template is equivalent to calling
+ # <tt>translate("people.index.foo")</tt>. This makes it less
+ # repetitive to translate many keys within the same partial and provides
+ # a convention to scope keys consistently.
+ #
+ # Third, the translation will be marked as <tt>html_safe</tt> if the key
+ # has the suffix "_html" or the last element of the key is "html". Calling
+ # <tt>translate("footer_html")</tt> or <tt>translate("footer.html")</tt>
+ # will return an HTML safe string that won't be escaped by other HTML
+ # helper methods. This naming convention helps to identify translations
+ # that include HTML tags so that you know what kind of output to expect
+ # when you call translate in a template and translators know which keys
+ # they can provide HTML values for.
def translate(key, options = {})
options = options.dup
has_default = options.has_key?(:default)
diff --git a/actionview/test/activerecord/form_helper_activerecord_test.rb b/actionview/test/activerecord/form_helper_activerecord_test.rb
index 0a62f49f35..2769b97445 100644
--- a/actionview/test/activerecord/form_helper_activerecord_test.rb
+++ b/actionview/test/activerecord/form_helper_activerecord_test.rb
@@ -35,10 +35,6 @@ class FormHelperActiveRecordTest < ActionView::TestCase
end
end
- def _routes
- Routes
- end
-
include Routes.url_helpers
def test_nested_fields_for_with_child_index_option_override_on_a_nested_attributes_collection_association
diff --git a/actionview/test/template/capture_helper_test.rb b/actionview/test/template/capture_helper_test.rb
index f213da5934..1e099d482c 100644
--- a/actionview/test/template/capture_helper_test.rb
+++ b/actionview/test/template/capture_helper_test.rb
@@ -210,10 +210,4 @@ class CaptureHelperTest < ActionView::TestCase
def alt_encoding(output_buffer)
output_buffer.encoding == Encoding::US_ASCII ? Encoding::UTF_8 : Encoding::US_ASCII
end
-
- def view_with_controller
- TestController.new.view_context.tap do |view|
- view.output_buffer = ActionView::OutputBuffer.new
- end
- end
end
diff --git a/actionview/test/template/date_helper_test.rb b/actionview/test/template/date_helper_test.rb
index bfb073680e..9212420ec9 100644
--- a/actionview/test/template/date_helper_test.rb
+++ b/actionview/test/template/date_helper_test.rb
@@ -130,7 +130,7 @@ class DateHelperTest < ActionView::TestCase
def test_distance_in_words_with_mathn_required
# test we avoid Integer#/ (redefined by mathn)
- require 'mathn'
+ silence_warnings { require "mathn" }
from = Time.utc(2004, 6, 6, 21, 45, 0)
assert_distance_of_time_in_words(from)
end
@@ -3217,12 +3217,4 @@ class DateHelperTest < ActionView::TestCase
expected = '<time datetime="2013-02-20T00:00:00+00:00">20 Feb 00:00</time>'
assert_equal expected, time_tag(time, :format => :short)
end
-
- protected
- def with_env_tz(new_tz = 'US/Eastern')
- old_tz, ENV['TZ'] = ENV['TZ'], new_tz
- yield
- ensure
- old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ')
- end
end
diff --git a/actionview/test/template/url_helper_test.rb b/actionview/test/template/url_helper_test.rb
index ef4df0407a..1a4d4f6a10 100644
--- a/actionview/test/template/url_helper_test.rb
+++ b/actionview/test/template/url_helper_test.rb
@@ -379,6 +379,11 @@ class UrlHelperTest < ActiveSupport::TestCase
assert_dom_equal %{<a href="/">Listing</a>}, link_to_if(true, "Listing", url_hash)
end
+ def test_link_to_if_with_block
+ assert_equal "Block Showing", link_to_if(false, url_hash) { "Block Showing" }
+ assert_dom_equal %{<a href="/">Block Listing</a>}, link_to_if(true, url_hash) { "Block Listing" }
+ end
+
def request_for_url(url, opts = {})
env = Rack::MockRequest.env_for("http://www.example.com#{url}", opts)
ActionDispatch::Request.new(env)
diff --git a/activejob/CHANGELOG.md b/activejob/CHANGELOG.md
index 85a437a1dd..1c55c1a4b8 100644
--- a/activejob/CHANGELOG.md
+++ b/activejob/CHANGELOG.md
@@ -1,3 +1,9 @@
+* `assert_enqueued_jobs` and `assert_performed_jobs` in block form use the
+ given number as expected value. This makes the error message much easier to
+ understand.
+
+ *y-yagi*
+
* A generated job now inherents from `app/jobs/application_job.rb` by default.
*Jeroen van Baarsen*
diff --git a/activejob/lib/active_job/arguments.rb b/activejob/lib/active_job/arguments.rb
index 622c37098e..cdb37879b6 100644
--- a/activejob/lib/active_job/arguments.rb
+++ b/activejob/lib/active_job/arguments.rb
@@ -5,6 +5,8 @@ module ActiveJob
#
# Wraps the original exception raised as +original_exception+.
class DeserializationError < StandardError
+ # The original exception that was raised during deserialization of job
+ # arguments.
attr_reader :original_exception
def initialize(e) #:nodoc:
@@ -24,6 +26,7 @@ module ActiveJob
module Arguments
extend self
+ # :nodoc:
TYPE_WHITELIST = [ NilClass, Fixnum, Float, String, TrueClass, FalseClass, Bignum ]
# Serializes a set of arguments. Whitelisted types are returned
@@ -43,8 +46,11 @@ module ActiveJob
end
private
+ # :nodoc:
GLOBALID_KEY = '_aj_globalid'.freeze
+ # :nodoc:
SYMBOL_KEYS_KEY = '_aj_symbol_keys'.freeze
+ # :nodoc:
WITH_INDIFFERENT_ACCESS_KEY = '_aj_hash_with_indifferent_access'.freeze
private_constant :GLOBALID_KEY, :SYMBOL_KEYS_KEY, :WITH_INDIFFERENT_ACCESS_KEY
@@ -113,6 +119,7 @@ module ActiveJob
result
end
+ # :nodoc:
RESERVED_KEYS = [
GLOBALID_KEY, GLOBALID_KEY.to_sym,
SYMBOL_KEYS_KEY, SYMBOL_KEYS_KEY.to_sym,
diff --git a/activejob/lib/active_job/core.rb b/activejob/lib/active_job/core.rb
index ddd7d1361c..acdfcdc791 100644
--- a/activejob/lib/active_job/core.rb
+++ b/activejob/lib/active_job/core.rb
@@ -1,4 +1,6 @@
module ActiveJob
+ # Provides general behavior that will be included into every Active Job
+ # object that inherits from ActiveJob::Base.
module Core
extend ActiveSupport::Concern
diff --git a/activejob/lib/active_job/enqueuing.rb b/activejob/lib/active_job/enqueuing.rb
index 430c17e1bf..98d92385dd 100644
--- a/activejob/lib/active_job/enqueuing.rb
+++ b/activejob/lib/active_job/enqueuing.rb
@@ -1,6 +1,7 @@
require 'active_job/arguments'
module ActiveJob
+ # Provides behavior for enqueuing and retrying jobs.
module Enqueuing
extend ActiveSupport::Concern
diff --git a/activejob/lib/active_job/queue_adapters.rb b/activejob/lib/active_job/queue_adapters.rb
index 8aa85979f6..bd69e525bb 100644
--- a/activejob/lib/active_job/queue_adapters.rb
+++ b/activejob/lib/active_job/queue_adapters.rb
@@ -27,7 +27,6 @@ module ActiveJob
# | Sneakers | Yes | Yes | No | Queue | Queue | No |
# | Sucker Punch | Yes | Yes | No | No | No | No |
# | Active Job Inline | No | Yes | N/A | N/A | N/A | N/A |
- # | Active Job | Yes | Yes | Yes | No | No | No |
#
# ==== Async
#
diff --git a/activejob/lib/active_job/queue_adapters/inline_adapter.rb b/activejob/lib/active_job/queue_adapters/inline_adapter.rb
index 1d06324c18..8ad5f4de07 100644
--- a/activejob/lib/active_job/queue_adapters/inline_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/inline_adapter.rb
@@ -14,7 +14,7 @@ module ActiveJob
end
def enqueue_at(*) #:nodoc:
- raise NotImplementedError.new("Use a queueing backend to enqueue jobs in the future. Read more at http://guides.rubyonrails.org/active_job_basics.html")
+ raise NotImplementedError, "Use a queueing backend to enqueue jobs in the future. Read more at http://guides.rubyonrails.org/active_job_basics.html"
end
end
end
diff --git a/activejob/lib/active_job/queue_adapters/qu_adapter.rb b/activejob/lib/active_job/queue_adapters/qu_adapter.rb
index 94584ef9d8..36f4c14911 100644
--- a/activejob/lib/active_job/queue_adapters/qu_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/qu_adapter.rb
@@ -23,7 +23,7 @@ module ActiveJob
end
def enqueue_at(job, timestamp, *args) #:nodoc:
- raise NotImplementedError
+ raise NotImplementedError, "This queueing backend does not support scheduling jobs. To see what features are supported go to http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters.html"
end
class JobWrapper < Qu::Job #:nodoc:
diff --git a/activejob/lib/active_job/queue_adapters/sneakers_adapter.rb b/activejob/lib/active_job/queue_adapters/sneakers_adapter.rb
index f5737487ca..f102c6567e 100644
--- a/activejob/lib/active_job/queue_adapters/sneakers_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/sneakers_adapter.rb
@@ -28,7 +28,7 @@ module ActiveJob
end
def enqueue_at(job, timestamp) #:nodoc:
- raise NotImplementedError
+ raise NotImplementedError, "This queueing backend does not support scheduling jobs. To see what features are supported go to http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters.html"
end
class JobWrapper #:nodoc:
diff --git a/activejob/lib/active_job/queue_adapters/sucker_punch_adapter.rb b/activejob/lib/active_job/queue_adapters/sucker_punch_adapter.rb
index 64c93e8198..c6c35f8ab4 100644
--- a/activejob/lib/active_job/queue_adapters/sucker_punch_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/sucker_punch_adapter.rb
@@ -23,7 +23,7 @@ module ActiveJob
end
def enqueue_at(job, timestamp) #:nodoc:
- raise NotImplementedError
+ raise NotImplementedError, "This queueing backend does not support scheduling jobs. To see what features are supported go to http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters.html"
end
class JobWrapper #:nodoc:
diff --git a/activejob/lib/active_job/test_helper.rb b/activejob/lib/active_job/test_helper.rb
index 4efb4b72d2..9b307e8dc8 100644
--- a/activejob/lib/active_job/test_helper.rb
+++ b/activejob/lib/active_job/test_helper.rb
@@ -68,7 +68,7 @@ module ActiveJob
original_count = enqueued_jobs_size(only: only)
yield
new_count = enqueued_jobs_size(only: only)
- assert_equal original_count + number, new_count, "#{number} jobs expected, but #{new_count - original_count} were enqueued"
+ assert_equal number, new_count - original_count, "#{number} jobs expected, but #{new_count - original_count} were enqueued"
else
actual_count = enqueued_jobs_size(only: only)
assert_equal number, actual_count, "#{number} jobs expected, but #{actual_count} were enqueued"
@@ -164,7 +164,7 @@ module ActiveJob
original_count = performed_jobs.size
perform_enqueued_jobs(only: only) { yield }
new_count = performed_jobs.size
- assert_equal original_count + number, new_count,
+ assert_equal number, new_count - original_count,
"#{number} jobs expected, but #{new_count - original_count} were performed"
else
performed_jobs_size = performed_jobs.size
diff --git a/activejob/lib/rails/generators/job/job_generator.rb b/activejob/lib/rails/generators/job/job_generator.rb
index 86e4c5266c..2115fb9f71 100644
--- a/activejob/lib/rails/generators/job/job_generator.rb
+++ b/activejob/lib/rails/generators/job/job_generator.rb
@@ -1,6 +1,6 @@
require 'rails/generators/named_base'
-module Rails
+module Rails # :nodoc:
module Generators # :nodoc:
class JobGenerator < Rails::Generators::NamedBase # :nodoc:
desc 'This generator creates an active job file at app/jobs'
diff --git a/activejob/test/cases/test_helper_test.rb b/activejob/test/cases/test_helper_test.rb
index 19a2820a6e..04c4c446e2 100644
--- a/activejob/test/cases/test_helper_test.rb
+++ b/activejob/test/cases/test_helper_test.rb
@@ -31,6 +31,17 @@ class EnqueuedJobsTest < ActiveJob::TestCase
end
end
+ def test_assert_enqueued_jobs_message
+ HelloJob.perform_later('sean')
+ e = assert_raises Minitest::Assertion do
+ assert_enqueued_jobs 2 do
+ HelloJob.perform_later('sean')
+ end
+ end
+ assert_match "Expected: 2", e.message
+ assert_match "Actual: 1", e.message
+ end
+
def test_assert_enqueued_jobs_with_no_block
assert_nothing_raised do
HelloJob.perform_later('rafael')
@@ -230,6 +241,17 @@ class PerformedJobsTest < ActiveJob::TestCase
end
end
+ def test_assert_performed_jobs_message
+ HelloJob.perform_later('sean')
+ e = assert_raises Minitest::Assertion do
+ assert_performed_jobs 2 do
+ HelloJob.perform_later('sean')
+ end
+ end
+ assert_match "Expected: 2", e.message
+ assert_match "Actual: 1", e.message
+ end
+
def test_assert_performed_jobs_with_no_block
assert_nothing_raised do
perform_enqueued_jobs do
diff --git a/activejob/test/integration/queuing_test.rb b/activejob/test/integration/queuing_test.rb
index 3075df607b..09f5c329cc 100644
--- a/activejob/test/integration/queuing_test.rb
+++ b/activejob/test/integration/queuing_test.rb
@@ -46,7 +46,7 @@ class QueuingTest < ActiveSupport::TestCase
test 'should run job enqueued in the future at the specified time' do
begin
- TestJob.set(wait: 3.seconds).perform_later @id
+ TestJob.set(wait: 5.seconds).perform_later @id
wait_for_jobs_to_finish_for(2.seconds)
assert_not job_executed
wait_for_jobs_to_finish_for(10.seconds)
diff --git a/activemodel/CHANGELOG.md b/activemodel/CHANGELOG.md
index 32a2cb4517..4961d69d2a 100644
--- a/activemodel/CHANGELOG.md
+++ b/activemodel/CHANGELOG.md
@@ -1,3 +1,12 @@
+* Add `ActiveModel::Dirty#[attr_name]_previously_changed?` and
+ `ActiveModel::Dirty#[attr_name]_previous_change` to improve access
+ to recorded changes after the model has been saved.
+
+ It makes the dirty-attributes query methods consistent before and after
+ saving.
+
+ *Fernando Tapia Rico*
+
* Deprecate the `:tokenizer` option for `validates_length_of`, in favor of
plain Ruby.
@@ -13,7 +22,7 @@
*Wojciech Wnętrzak*
-* Allow symbol as values for `tokenize` of `LengthValidator`
+* Allow symbol as values for `tokenize` of `LengthValidator`.
*Kensuke Naito*
diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb
index 96be551264..ff7280f9e5 100644
--- a/activemodel/lib/active_model/attribute_methods.rb
+++ b/activemodel/lib/active_model/attribute_methods.rb
@@ -23,7 +23,7 @@ module ActiveModel
# The requirements to implement <tt>ActiveModel::AttributeMethods</tt> are to:
#
# * <tt>include ActiveModel::AttributeMethods</tt> in your class.
- # * Call each of its method you want to add, such as +attribute_method_suffix+
+ # * Call each of its methods you want to add, such as +attribute_method_suffix+
# or +attribute_method_prefix+.
# * Call +define_attribute_methods+ after the other methods are called.
# * Define the various generic +_attribute+ methods that you have declared.
@@ -227,7 +227,7 @@ module ActiveModel
# Declares the attributes that should be prefixed and suffixed by
# ActiveModel::AttributeMethods.
#
- # To use, pass attribute names (as strings or symbols), be sure to declare
+ # To use, pass attribute names (as strings or symbols). Be sure to declare
# +define_attribute_methods+ after you define any prefix, suffix or affix
# methods, or they will not hook in.
#
@@ -239,7 +239,7 @@ module ActiveModel
#
# # Call to define_attribute_methods must appear after the
# # attribute_method_prefix, attribute_method_suffix or
- # # attribute_method_affix declares.
+ # # attribute_method_affix declarations.
# define_attribute_methods :name, :age, :address
#
# private
@@ -255,7 +255,7 @@ module ActiveModel
# Declares an attribute that should be prefixed and suffixed by
# ActiveModel::AttributeMethods.
#
- # To use, pass an attribute name (as string or symbol), be sure to declare
+ # To use, pass an attribute name (as string or symbol). Be sure to declare
# +define_attribute_method+ after you define any prefix, suffix or affix
# method, or they will not hook in.
#
@@ -267,7 +267,7 @@ module ActiveModel
#
# # Call to define_attribute_method must appear after the
# # attribute_method_prefix, attribute_method_suffix or
- # # attribute_method_affix declares.
+ # # attribute_method_affix declarations.
# define_attribute_method :name
#
# private
@@ -363,7 +363,7 @@ module ActiveModel
end
# Define a method `name` in `mod` that dispatches to `send`
- # using the given `extra` args. This fallbacks `define_method`
+ # using the given `extra` args. This falls back on `define_method`
# and `send` if the given names cannot be compiled.
def define_proxy_call(include_private, mod, name, send, *extra) #:nodoc:
defn = if name =~ NAME_COMPILABLE_REGEXP
@@ -419,7 +419,7 @@ module ActiveModel
# returned by <tt>attributes</tt>, as though they were first-class
# methods. So a +Person+ class with a +name+ attribute can for example use
# <tt>Person#name</tt> and <tt>Person#name=</tt> and never directly use
- # the attributes hash -- except for multiple assigns with
+ # the attributes hash -- except for multiple assignments with
# <tt>ActiveRecord::Base#attributes=</tt>.
#
# It's also possible to instantiate related objects, so a <tt>Client</tt>
diff --git a/activemodel/lib/active_model/dirty.rb b/activemodel/lib/active_model/dirty.rb
index c03e5fac79..c0fc507286 100644
--- a/activemodel/lib/active_model/dirty.rb
+++ b/activemodel/lib/active_model/dirty.rb
@@ -76,9 +76,11 @@ module ActiveModel
#
# Reset the changes:
#
- # person.previous_changes # => {"name" => ["Uncle Bob", "Bill"]}
+ # person.previous_changes # => {"name" => ["Uncle Bob", "Bill"]}
+ # person.name_previously_changed? # => true
+ # person.name_previous_change # => ["Uncle Bob", "Bill"]
# person.reload!
- # person.previous_changes # => {}
+ # person.previous_changes # => {}
#
# Rollback the changes:
#
@@ -115,6 +117,7 @@ module ActiveModel
included do
attribute_method_suffix '_changed?', '_change', '_will_change!', '_was'
+ attribute_method_suffix '_previously_changed?', '_previous_change'
attribute_method_affix prefix: 'restore_', suffix: '!'
end
@@ -179,6 +182,11 @@ module ActiveModel
attribute_changed?(attr) ? changed_attributes[attr] : __send__(attr)
end
+ # Handles <tt>*_previously_changed?</tt> for +method_missing+.
+ def attribute_previously_changed?(attr, options = {}) #:nodoc:
+ previous_changes_include?(attr)
+ end
+
# Restore all previous data of the provided attributes.
def restore_attributes(attributes = changed)
attributes.each { |attr| restore_attribute! attr }
@@ -192,6 +200,12 @@ module ActiveModel
end
alias attribute_changed_by_setter? changes_include?
+ # Returns +true+ if attr_name were changed before the model was saved,
+ # +false+ otherwise.
+ def previous_changes_include?(attr_name)
+ @previously_changed.include?(attr_name)
+ end
+
# Removes current changes and makes them accessible through +previous_changes+.
def changes_applied # :doc:
@previously_changed = changes
@@ -209,6 +223,11 @@ module ActiveModel
[changed_attributes[attr], __send__(attr)] if attribute_changed?(attr)
end
+ # Handles <tt>*_previous_change</tt> for +method_missing+.
+ def attribute_previous_change(attr)
+ @previously_changed[attr] if attribute_previously_changed?(attr)
+ end
+
# Handles <tt>*_will_change!</tt> for +method_missing+.
def attribute_will_change!(attr)
return if attribute_changed?(attr)
diff --git a/activemodel/lib/active_model/validations.rb b/activemodel/lib/active_model/validations.rb
index 74d60327d6..c1019169e1 100644
--- a/activemodel/lib/active_model/validations.rb
+++ b/activemodel/lib/active_model/validations.rb
@@ -87,6 +87,7 @@ module ActiveModel
validates_with BlockValidator, _merge_attributes(attr_names), &block
end
+ # :nodoc:
VALID_OPTIONS_FOR_VALIDATE = [:on, :if, :unless, :prepend].freeze
# Adds a validation method or block to the class. This is useful when
diff --git a/activemodel/test/cases/dirty_test.rb b/activemodel/test/cases/dirty_test.rb
index 66ed8a350a..d17a12ad12 100644
--- a/activemodel/test/cases/dirty_test.rb
+++ b/activemodel/test/cases/dirty_test.rb
@@ -137,6 +137,19 @@ class DirtyTest < ActiveModel::TestCase
assert_equal [nil, "Jericho Cane"], @model.previous_changes['name']
end
+ test "setting new attributes should not affect previous changes" do
+ @model.name = "Jericho Cane"
+ @model.save
+ @model.name = "DudeFella ManGuy"
+ assert_equal [nil, "Jericho Cane"], @model.name_previous_change
+ end
+
+ test "saving should preserve model's previous changed status" do
+ @model.name = "Jericho Cane"
+ @model.save
+ assert @model.name_previously_changed?
+ end
+
test "previous value is preserved when changed after save" do
assert_equal({}, @model.changed_attributes)
@model.name = "Paul"
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 3d7e175e2e..b6a943eca7 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,27 @@
+* Make `unscope` aware of "less than" and "greater than" conditions.
+
+ *TAKAHASHI Kazuaki*
+
+* `find_by` and `find_by!` raise `ArgumentError` when called without
+ arguments.
+
+ *Kohei Suzuki*
+
+* Revert behavior of `db:schema:load` back to loading the full
+ environment. This ensures that initializers are run.
+
+ Fixes #19545.
+
+ *Yves Senn*
+
+* Fix missing index when using `timestamps` with the `index` option.
+
+ The `index` option used with `timestamps` should be passed to both
+ `column` definitions for `created_at` and `updated_at` rather than just
+ the first.
+
+ *Paul Mucur*
+
* Rename `:class` to `:anonymous_class` in association options.
Fixes #19659.
@@ -34,20 +58,22 @@
*Mehmet Emin İNAÇ*
-* Reduce memory usage from loading types on pg.
+* Reduce memory usage from loading types on PostgreSQL.
Fixes #19578.
*Sean Griffin*
-* Add `config.active_record.warn_on_records_fetched_greater_than` option
+* Add `config.active_record.warn_on_records_fetched_greater_than` option.
When set to an integer, a warning will be logged whenever a result set
- larger than the specified size is returned by a query. Fixes #16463
+ larger than the specified size is returned by a query.
+
+ Fixes #16463.
*Jason Nochlin*
-* Ignore psqlrc when loading database structure.
+* Ignore `.psqlrc` when loading database structure.
*Jason Weathered*
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb
index c0a2111571..30b2fca2ca 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb
@@ -18,9 +18,9 @@ module ActiveRecord
end
# Returns the maximum allowed length for an index name. This
- # limit is enforced by rails and Is less than or equal to
+ # limit is enforced by \Rails and is less than or equal to
# <tt>index_name_length</tt>. The gap between
- # <tt>index_name_length</tt> is to allow internal rails
+ # <tt>index_name_length</tt> is to allow internal \Rails
# operations to use prefixes in temporary operations.
def allowed_index_name_length
index_name_length
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 cb83d0022c..4761024ad0 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -359,6 +359,7 @@ module ActiveRecord
def column(name, type, options = {})
name = name.to_s
type = type.to_sym
+ options = options.dup
if @columns_hash[name] && @columns_hash[name].primary_key?
raise ArgumentError, "you can't redefine the primary key column '#{name}'. To define a custom primary key, pass { id: false } to create_table."
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 ecb4868c13..c4a37f8538 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -120,6 +120,8 @@ module ActiveRecord
# [<tt>:id</tt>]
# Whether to automatically add a primary key column. Defaults to true.
# Join tables for +has_and_belongs_to_many+ should set it to false.
+ #
+ # A Symbol can be used to specify the type of the generated primary key column.
# [<tt>:primary_key</tt>]
# The name of the primary key, if one is to be added automatically.
# Defaults to +id+. If <tt>:id</tt> is false this option is ignored.
@@ -163,6 +165,19 @@ module ActiveRecord
# name varchar(80)
# )
#
+ # ====== Change the primary key column type
+ #
+ # create_table(:tags, id: :string) do |t|
+ # t.column :label, :string
+ # end
+ #
+ # generates:
+ #
+ # CREATE TABLE tags (
+ # id varchar PRIMARY KEY,
+ # label varchar
+ # )
+ #
# ====== Do not add a primary key column
#
# create_table(:categories_suppliers, id: false) do |t|
@@ -1034,7 +1049,7 @@ module ActiveRecord
end
end
- def validate_index_length!(table_name, new_name)
+ def validate_index_length!(table_name, new_name) # :nodoc:
if new_name.length > allowed_index_name_length
raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{allowed_index_name_length} characters"
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
index 45b935f1d6..18febf66b4 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -5,8 +5,10 @@ require 'active_support/core_ext/hash/keys'
gem 'mysql', '~> 2.9'
require 'mysql'
-class Mysql
+class Mysql # :nodoc: all
class Time
+ # Used for casting DateTime fields to a MySQL friendly Time.
+ # This was documented in 48498da0dfed5239ea1eafb243ce47d7e3ce9e8e
def to_date
Date.new(year, month, day)
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
index 168180cfd3..70fc1a6bd8 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
@@ -100,6 +100,7 @@ module ActiveRecord
SQL
end
+ # Verifies existence of an index with a given name.
def index_name_exists?(table_name, index_name, default)
exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0
SELECT COUNT(*)
@@ -462,6 +463,8 @@ module ActiveRecord
execute "DROP INDEX #{quote_table_name(index_name)}"
end
+ # Renames an index of a table. Raises error if length of new
+ # index name is greater than allowed limit.
def rename_index(table_name, old_name, new_name)
validate_index_length!(table_name, new_name)
diff --git a/activerecord/lib/active_record/enum.rb b/activerecord/lib/active_record/enum.rb
index ea88983917..2b99899e42 100644
--- a/activerecord/lib/active_record/enum.rb
+++ b/activerecord/lib/active_record/enum.rb
@@ -32,7 +32,7 @@ module ActiveRecord
# Conversation.active
# Conversation.archived
#
- # Of course, you can also query them directly if the scopes doesn't fit your
+ # Of course, you can also query them directly if the scopes don't fit your
# needs:
#
# Conversation.where(status: [:active, :archived])
diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb
index 084ef397a8..90e37e80d2 100644
--- a/activerecord/lib/active_record/nested_attributes.rb
+++ b/activerecord/lib/active_record/nested_attributes.rb
@@ -147,8 +147,8 @@ module ActiveRecord
# has_many :posts
# accepts_nested_attributes_for :posts, reject_if: :reject_posts
#
- # def reject_posts(attributed)
- # attributed['title'].blank?
+ # def reject_posts(attributes)
+ # attributes['title'].blank?
# end
# end
#
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index ae1c326d95..da8f4d027a 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -205,7 +205,9 @@ module ActiveRecord
# instance using the companies/company partial instead of clients/client.
#
# Note: The new instance will share a link to the same attributes as the original class.
- # So any change to the attributes in either instance will affect the other.
+ # Therefore the sti column value will still be the same.
+ # Any change to the attributes on either instance will affect both instances.
+ # If you want to change the sti column as well, use +becomes!+ instead.
def becomes(klass)
became = klass.new
became.instance_variable_set("@attributes", @attributes)
@@ -428,7 +430,7 @@ module ActiveRecord
self
end
- # Saves the record with the updated_at/on attributes set to the current time
+ # Saves the record with the updated_at/on attributes set to the current time
# or the time specified.
# Please note that no validation is performed and only the +after_touch+,
# +after_commit+ and +after_rollback+ callbacks are executed.
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index 2591e7492d..d168786e71 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -240,7 +240,7 @@ db_namespace = namespace :db do
end
desc 'Load a schema.rb file into the database'
- task :load => [:load_config] do
+ task :load => [:environment, :load_config] do
ActiveRecord::Tasks::DatabaseTasks.load_schema_current(:ruby, ENV['SCHEMA'])
end
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 6a3a56f1cc..576a32bf75 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -77,16 +77,16 @@ module ActiveRecord
#
# Post.find_by name: 'Spartacus', rating: 4
# Post.find_by "published_at < ?", 2.weeks.ago
- def find_by(*args)
- where(*args).take
+ def find_by(arg, *args)
+ where(arg, *args).take
rescue RangeError
nil
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).take!
+ def find_by!(arg, *args)
+ where(arg, *args).take!
rescue RangeError
raise RecordNotFound, "Couldn't find #{@klass.name} with an out of range value"
end
diff --git a/activerecord/lib/active_record/relation/where_clause.rb b/activerecord/lib/active_record/relation/where_clause.rb
index f9b9e640ec..1f000b3f0f 100644
--- a/activerecord/lib/active_record/relation/where_clause.rb
+++ b/activerecord/lib/active_record/relation/where_clause.rb
@@ -135,7 +135,7 @@ module ActiveRecord
def predicates_except(columns)
predicates.reject do |node|
case node
- when Arel::Nodes::Between, Arel::Nodes::In, Arel::Nodes::NotIn, Arel::Nodes::Equality, Arel::Nodes::NotEqual, Arel::Nodes::LessThanOrEqual, Arel::Nodes::GreaterThanOrEqual
+ when Arel::Nodes::Between, Arel::Nodes::In, Arel::Nodes::NotIn, Arel::Nodes::Equality, Arel::Nodes::NotEqual, Arel::Nodes::LessThan, Arel::Nodes::LessThanOrEqual, Arel::Nodes::GreaterThan, Arel::Nodes::GreaterThanOrEqual
subrelation = (node.left.kind_of?(Arel::Attributes::Attribute) ? node.left : node.right)
columns.include?(subrelation.name.to_s)
end
diff --git a/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb b/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb
index 7a3c6f5e95..0d57de4d65 100644
--- a/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb
+++ b/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb
@@ -14,10 +14,9 @@ module ActiveRecord
protected
attr_reader :migration_action, :join_tables
- # sets the default migration template that is being used for the generation of the migration
- # depending on the arguments which would be sent out in the command line, the migration template
- # and the table name instance variables are setup.
-
+ # Sets the default migration template that is being used for the generation of the migration.
+ # Depending on command line arguments, the migration template and the table name instance
+ # variables are set up.
def set_local_assigns!
@migration_template = "migration.rb"
case file_name
@@ -56,6 +55,8 @@ module ActiveRecord
attributes.select { |a| !a.reference? && a.has_index? }
end
+ # A migration file name can only contain underscores (_), lowercase characters,
+ # and numbers 0-9. Any other file name will raise an IllegalMigrationNameError.
def validate_file_name!
unless file_name =~ /^[_a-z0-9]+$/
raise IllegalMigrationNameError.new(file_name)
diff --git a/activerecord/test/cases/disconnected_test.rb b/activerecord/test/cases/disconnected_test.rb
index 55f0e51717..c25089a420 100644
--- a/activerecord/test/cases/disconnected_test.rb
+++ b/activerecord/test/cases/disconnected_test.rb
@@ -21,7 +21,9 @@ class TestDisconnectedAdapter < ActiveRecord::TestCase
@connection.execute "SELECT count(*) from products"
@connection.disconnect!
assert_raises(ActiveRecord::StatementInvalid) do
- @connection.execute "SELECT count(*) from products"
+ silence_warnings do
+ @connection.execute "SELECT count(*) from products"
+ end
end
end
end
diff --git a/activerecord/test/cases/migration/references_foreign_key_test.rb b/activerecord/test/cases/migration/references_foreign_key_test.rb
index 87348d0f90..1594f99852 100644
--- a/activerecord/test/cases/migration/references_foreign_key_test.rb
+++ b/activerecord/test/cases/migration/references_foreign_key_test.rb
@@ -130,4 +130,24 @@ module ActiveRecord
end
end
end
+else
+class ReferencesWithoutForeignKeySupportTest < ActiveRecord::TestCase
+ setup do
+ @connection = ActiveRecord::Base.connection
+ @connection.create_table(:testing_parents, force: true)
+ end
+
+ teardown do
+ @connection.drop_table("testings", if_exists: true)
+ @connection.drop_table("testing_parents", if_exists: true)
+ end
+
+ test "ignores foreign keys defined with the table" do
+ @connection.create_table :testings do |t|
+ t.references :testing_parent, foreign_key: true
+ end
+
+ assert_includes @connection.tables, "testings"
+ end
+end
end
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index 0cf44388fa..b8e2041b6d 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -1657,6 +1657,10 @@ class RelationTest < ActiveRecord::TestCase
assert_sql(/^((?!ORDER).)*$/) { Post.all.find_by(author_id: 2) }
end
+ test "find_by requires at least one argument" do
+ assert_raises(ArgumentError) { Post.all.find_by }
+ 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
@@ -1679,6 +1683,10 @@ class RelationTest < ActiveRecord::TestCase
end
end
+ test "find_by! requires at least one argument" do
+ assert_raises(ArgumentError) { Post.all.find_by! }
+ end
+
test "loaded relations cannot be mutated by multi value methods" do
relation = Post.all
relation.to_a
diff --git a/activerecord/test/cases/scoping/default_scoping_test.rb b/activerecord/test/cases/scoping/default_scoping_test.rb
index 4137b20c4a..0dbc60940e 100644
--- a/activerecord/test/cases/scoping/default_scoping_test.rb
+++ b/activerecord/test/cases/scoping/default_scoping_test.rb
@@ -153,6 +153,18 @@ class DefaultScopingTest < ActiveRecord::TestCase
assert_equal expected_7, received_7
end
+ def test_unscope_comparison_where_clauses
+ # unscoped for WHERE (`developers`.`id` <= 2)
+ expected = Developer.order('salary DESC').collect(&:name)
+ received = DeveloperOrderedBySalary.where(id: -Float::INFINITY..2).unscope(where: :id).collect { |dev| dev.name }
+ assert_equal expected, received
+
+ # unscoped for WHERE (`developers`.`id` < 2)
+ expected = Developer.order('salary DESC').collect(&:name)
+ received = DeveloperOrderedBySalary.where(id: -Float::INFINITY...2).unscope(where: :id).collect { |dev| dev.name }
+ assert_equal expected, received
+ end
+
def test_unscope_multiple_where_clauses
expected = Developer.order('salary DESC').collect(&:name)
received = DeveloperOrderedBySalary.where(name: 'Jamis').where(id: 1).unscope(where: [:name, :id]).collect(&:name)
diff --git a/activerecord/test/cases/timestamp_test.rb b/activerecord/test/cases/timestamp_test.rb
index 7c89b4b9e8..5dab32995c 100644
--- a/activerecord/test/cases/timestamp_test.rb
+++ b/activerecord/test/cases/timestamp_test.rb
@@ -446,6 +446,17 @@ class TimestampTest < ActiveRecord::TestCase
toy = Toy.first
assert_equal [:created_at, :updated_at], toy.send(:all_timestamp_attributes_in_model)
end
+
+ def test_index_is_created_for_both_timestamps
+ ActiveRecord::Base.connection.create_table(:foos, force: true) do |t|
+ t.timestamps(:foos, null: true, index: true)
+ end
+
+ indexes = ActiveRecord::Base.connection.indexes('foos')
+ assert_equal ['created_at', 'updated_at'], indexes.flat_map(&:columns).sort
+ ensure
+ ActiveRecord::Base.connection.drop_table(:foos)
+ end
end
class TimestampsWithoutTransactionTest < ActiveRecord::TestCase
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index 3ad2392365..ac27dc640e 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,3 +1,14 @@
+* Encoding ActiveSupport::TimeWithZone to YAML now preserves the timezone information.
+
+ Fixes #9183.
+
+ *Andrew White*
+
+* Added `ActiveSupport::TimeZone#strptime` to allow parsing times as if
+ from a given timezone.
+
+ *Paul A Jungwirth*
+
* `ActiveSupport::Callbacks#skip_callback` now raises an `ArgumentError` if
an unrecognized callback is removed.
@@ -23,7 +34,8 @@
*George Claghorn*
-* Deprecate `alias_method_chain` in favour of `Module#prepend` introduced in Ruby 2.0
+* Deprecate `alias_method_chain` in favour of `Module#prepend` introduced in
+ Ruby 2.0.
*Kir Shatrov*
diff --git a/activesupport/lib/active_support/core_ext/module/aliasing.rb b/activesupport/lib/active_support/core_ext/module/aliasing.rb
index a4c40b25ff..b6934b9c54 100644
--- a/activesupport/lib/active_support/core_ext/module/aliasing.rb
+++ b/activesupport/lib/active_support/core_ext/module/aliasing.rb
@@ -1,4 +1,7 @@
class Module
+ # NOTE: This method is deprecated. Please use <tt>Module#prepend</tt> that
+ # comes with Ruby 2.0 or newer instead.
+ #
# Encapsulates the common pattern of:
#
# alias_method :foo_without_feature, :foo
diff --git a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb
index a03a66b96b..dedcdfdb60 100644
--- a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb
+++ b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb
@@ -25,15 +25,17 @@ module ActiveSupport
end
end
- # This DeprecatedObjectProxy transforms object to deprecated object.
+ # DeprecatedObjectProxy transforms an object into a deprecated object. It takes an object,
+ # a deprecation message, and optionally a deprecator. The deprecator defaults to
+ # <tt>ActiveSupport::Deprecator</tt> if none is specified.
#
- # @old_object = DeprecatedObjectProxy.new(Object.new, "Don't use this object anymore!")
- # @old_object = DeprecatedObjectProxy.new(Object.new, "Don't use this object anymore!", deprecator_instance)
+ # deprecated_object = ActiveSupport::Deprecation::DeprecatedObjectProxy.new(Object.new, "This object is now deprecated")
+ # # => <Object:0x007fb9b34c34b0>
#
- # When someone executes any method except +inspect+ on proxy object this will
- # trigger +warn+ method on +deprecator_instance+.
- #
- # Default deprecator is <tt>ActiveSupport::Deprecation</tt>
+ # deprecated_object.to_s
+ # DEPRECATION WARNING: This object is now deprecated.
+ # (Backtrace)
+ # # => "<Object:0x007fb9b34c34b0>"
class DeprecatedObjectProxy < DeprecationProxy
def initialize(object, message, deprecator = ActiveSupport::Deprecation.instance)
@object = object
@@ -51,13 +53,15 @@ module ActiveSupport
end
end
- # This DeprecatedInstanceVariableProxy transforms instance variable to
- # deprecated instance variable.
+ # DeprecatedInstanceVariableProxy transforms an instance variable into a deprecated
+ # instance variable. It takes an instance of a class, a method on that class, and an
+ # instance variable. It optionally takes a deprecator as the last argument. The deprecator
+ # defaults to <tt>ActiveSupport::Deprecator</tt> if none is specified.
#
# class Example
- # def initialize(deprecator)
+ # def initialize
# @request = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(self, :request, :@request, deprecator)
- # @_request = :a_request
+ # @_request = :special_request
# end
#
# def request
@@ -69,12 +73,17 @@ module ActiveSupport
# end
# end
#
- # When someone execute any method on @request variable this will trigger
- # +warn+ method on +deprecator_instance+ and will fetch <tt>@_request</tt>
- # variable via +request+ method and execute the same method on non-proxy
- # instance variable.
+ # example = Example.new
+ # # => #<Example:0x007fb9b31090b8 @_request=:special_request, @request=:special_request>
+ #
+ # example.old_request.to_s
+ # # => DEPRECATION WARNING: @request is deprecated! Call request.to_s instead of
+ # @request.to_s
+ # (Bactrace information…)
+ # "special_request"
#
- # Default deprecator is <tt>ActiveSupport::Deprecation</tt>.
+ # example.request.to_s
+ # # => "special_request"
class DeprecatedInstanceVariableProxy < DeprecationProxy
def initialize(instance, method, var = "@#{method}", deprecator = ActiveSupport::Deprecation.instance)
@instance = instance
@@ -93,15 +102,22 @@ module ActiveSupport
end
end
- # This DeprecatedConstantProxy transforms constant to deprecated constant.
+ # DeprecatedConstantProxy transforms a constant into a deprecated constant. It takes the names of an old
+ # (deprecated) constant and a new contstant (both in string form), and optionally a deprecator. The
+ # deprecator defaults to <tt>ActiveSupport::Deprecator</tt> if none is specified. The deprecated constant
+ # now returns the return value of the new constant.
+ #
+ # PLANETS = %w(mercury venus earth mars jupiter saturn uranus neptune pluto)
#
- # OLD_CONST = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('OLD_CONST', 'NEW_CONST')
- # OLD_CONST = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('OLD_CONST', 'NEW_CONST', deprecator_instance)
+ # (In a later update, the orignal implementation of `PLANETS` has been removed.)
#
- # When someone use old constant this will trigger +warn+ method on
- # +deprecator_instance+.
+ # PLANETS_POST_2006 = %w(mercury venus earth mars jupiter saturn uranus neptune)
+ # PLANETS = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('PLANETS', 'PLANETS_POST_2006')
#
- # Default deprecator is <tt>ActiveSupport::Deprecation</tt>.
+ # PLANETS.map { |planet| planet.capitalize }
+ # # => DEPRECATION WARNING: PLANETS is deprecated! Use PLANETS_POST_2006 instead.
+ # (Bactrace information…)
+ # ["Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"]
class DeprecatedConstantProxy < DeprecationProxy
def initialize(old_const, new_const, deprecator = ActiveSupport::Deprecation.instance)
@old_const = old_const
diff --git a/activesupport/lib/active_support/rails.rb b/activesupport/lib/active_support/rails.rb
index b05c3ff126..c8e3a4bf53 100644
--- a/activesupport/lib/active_support/rails.rb
+++ b/activesupport/lib/active_support/rails.rb
@@ -1,8 +1,8 @@
# This is private interface.
#
# Rails components cherry pick from Active Support as needed, but there are a
-# few features that are used for sure some way or another and it is not worth
-# to put individual requires absolutely everywhere. Think blank? for example.
+# few features that are used for sure in some way or another and it is not worth
+# putting individual requires absolutely everywhere. Think blank? for example.
#
# This file is loaded by every Rails component except Active Support itself,
# but it does not belong to the Rails public interface. It is internal to
diff --git a/activesupport/lib/active_support/rescuable.rb b/activesupport/lib/active_support/rescuable.rb
index 67aac32742..fcf5553061 100644
--- a/activesupport/lib/active_support/rescuable.rb
+++ b/activesupport/lib/active_support/rescuable.rb
@@ -68,7 +68,7 @@ module ActiveSupport
raise ArgumentError, "#{klass} is neither an Exception nor a String"
end
- # put the new handler at the end because the list is read in reverse
+ # Put the new handler at the end because the list is read in reverse.
self.rescue_handlers += [[key, options[:with]]]
end
end
diff --git a/activesupport/lib/active_support/subscriber.rb b/activesupport/lib/active_support/subscriber.rb
index 8db423f0e9..1cd4b807ad 100644
--- a/activesupport/lib/active_support/subscriber.rb
+++ b/activesupport/lib/active_support/subscriber.rb
@@ -5,7 +5,7 @@ module ActiveSupport
# ActiveSupport::Notifications. The subscriber dispatches notifications to
# a registered object based on its given namespace.
#
- # An example would be Active Record subscriber responsible for collecting
+ # An example would be an Active Record subscriber responsible for collecting
# statistics about queries:
#
# module ActiveRecord
@@ -61,7 +61,7 @@ module ActiveSupport
pattern = "#{event}.#{namespace}"
- # don't add multiple subscribers (eg. if methods are redefined)
+ # Don't add multiple subscribers (eg. if methods are redefined).
return if subscriber.patterns.include?(pattern)
subscriber.patterns << pattern
diff --git a/activesupport/lib/active_support/testing/isolation.rb b/activesupport/lib/active_support/testing/isolation.rb
index 247df7423b..1de0a19998 100644
--- a/activesupport/lib/active_support/testing/isolation.rb
+++ b/activesupport/lib/active_support/testing/isolation.rb
@@ -69,17 +69,17 @@ module ActiveSupport
else
Tempfile.open("isolation") do |tmpfile|
env = {
- ISOLATION_TEST: self.class.name,
- ISOLATION_OUTPUT: tmpfile.path
+ 'ISOLATION_TEST' => self.class.name,
+ 'ISOLATION_OUTPUT' => tmpfile.path
}
load_paths = $-I.map {|p| "-I\"#{File.expand_path(p)}\"" }.join(" ")
orig_args = ORIG_ARGV.join(" ")
test_opts = "-n#{self.class.name}##{self.name}"
- command = "#{Gem.ruby} #{load_paths} #{$0} #{orig_args} #{test_opts}"
+ command = "#{Gem.ruby} #{load_paths} #{$0} '#{orig_args}' #{test_opts}"
# IO.popen lets us pass env in a cross-platform way
- child = IO.popen([env, command])
+ child = IO.popen(env, command)
begin
Process.wait(child.pid)
diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb
index c28de4e21c..b0d7f3299f 100644
--- a/activesupport/lib/active_support/time_with_zone.rb
+++ b/activesupport/lib/active_support/time_with_zone.rb
@@ -169,12 +169,13 @@ module ActiveSupport
end
end
- def encode_with(coder)
- if coder.respond_to?(:represent_object)
- coder.represent_object(nil, utc)
- else
- coder.represent_scalar(nil, utc.strftime("%Y-%m-%d %H:%M:%S.%9NZ"))
- end
+ def init_with(coder) #:nodoc:
+ initialize(coder['utc'], coder['zone'], coder['time'])
+ end
+
+ def encode_with(coder) #:nodoc:
+ coder.tag = '!ruby/object:ActiveSupport::TimeWithZone'
+ coder.map = { 'utc' => utc, 'zone' => time_zone, 'time' => time }
end
# Returns a string of the object's date and time in the format used by
diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb
index da39f0d245..2699a064d7 100644
--- a/activesupport/lib/active_support/values/time_zone.rb
+++ b/activesupport/lib/active_support/values/time_zone.rb
@@ -348,24 +348,31 @@ module ActiveSupport
#
# Time.zone.parse('Mar 2000') # => Wed, 01 Mar 2000 00:00:00 HST -10:00
def parse(str, now=now())
- parts = Date._parse(str, false)
- return if parts.empty?
-
- time = Time.new(
- parts.fetch(:year, now.year),
- parts.fetch(:mon, now.month),
- parts.fetch(:mday, parts[:year] || parts[:mon] ? 1 : now.day),
- parts.fetch(:hour, 0),
- parts.fetch(:min, 0),
- parts.fetch(:sec, 0) + parts.fetch(:sec_fraction, 0),
- parts.fetch(:offset, 0)
- )
-
- if parts[:offset]
- TimeWithZone.new(time.utc, self)
- else
- TimeWithZone.new(nil, self, time)
- end
+ parts_to_time(Date._parse(str, false), now)
+ end
+
+ # Parses +str+ according to +format+ and returns an ActiveSupport::TimeWithZone.
+ #
+ # Assumes that +str+ is a time in the time zone +self+,
+ # unless +format+ includes an explicit time zone.
+ # (This is the same behavior as +parse+.)
+ # In either case, the returned TimeWithZone has the timezone of +self+.
+ #
+ # Time.zone = 'Hawaii' # => "Hawaii"
+ # Time.zone.strptime('1999-12-31 14:00:00', '%Y-%m-%d %H:%M:%S') # => Fri, 31 Dec 1999 14:00:00 HST -10:00
+ #
+ # If upper components are missing from the string, they are supplied from
+ # TimeZone#now:
+ #
+ # Time.zone.now # => Fri, 31 Dec 1999 14:00:00 HST -10:00
+ # Time.zone.strptime('22:30:00', '%H:%M:%S') # => Fri, 31 Dec 1999 22:30:00 HST -10:00
+ #
+ # However, if the date component is not provided, but any other upper
+ # components are supplied, then the day of the month defaults to 1:
+ #
+ # Time.zone.strptime('Mar 2000', '%b %Y') # => Wed, 01 Mar 2000 00:00:00 HST -10:00
+ def strptime(str, format, now=now())
+ parts_to_time(DateTime._strptime(str, format), now)
end
# Returns an ActiveSupport::TimeWithZone instance representing the current
@@ -421,7 +428,36 @@ module ActiveSupport
tzinfo.periods_for_local(time)
end
+ def init_with(coder) #:nodoc:
+ initialize(coder['name'])
+ end
+
+ def encode_with(coder) #:nodoc:
+ coder.tag ="!ruby/object:#{self.class}"
+ coder.map = { 'name' => tzinfo.name }
+ end
+
private
+ def parts_to_time(parts, now)
+ return if parts.empty?
+
+ time = Time.new(
+ parts.fetch(:year, now.year),
+ parts.fetch(:mon, now.month),
+ parts.fetch(:mday, parts[:year] || parts[:mon] ? 1 : now.day),
+ parts.fetch(:hour, 0),
+ parts.fetch(:min, 0),
+ parts.fetch(:sec, 0) + parts.fetch(:sec_fraction, 0),
+ parts.fetch(:offset, 0)
+ )
+
+ if parts[:offset]
+ TimeWithZone.new(time.utc, self)
+ else
+ TimeWithZone.new(nil, self, time)
+ end
+ end
+
def time_now
Time.now
end
diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb
index 92c233d567..79d78c02cd 100644
--- a/activesupport/test/core_ext/time_with_zone_test.rb
+++ b/activesupport/test/core_ext/time_with_zone_test.rb
@@ -1,6 +1,7 @@
require 'abstract_unit'
require 'active_support/time'
require 'time_zone_test_helpers'
+require 'active_support/core_ext/string/strip'
class TimeWithZoneTest < ActiveSupport::TestCase
include TimeZoneTestHelpers
@@ -123,11 +124,53 @@ class TimeWithZoneTest < ActiveSupport::TestCase
end
def test_to_yaml
- assert_match(/^--- 2000-01-01 00:00:00(\.0+)?\s*Z\n/, @twz.to_yaml)
+ yaml = <<-EOF.strip_heredoc
+ --- !ruby/object:ActiveSupport::TimeWithZone
+ utc: 2000-01-01 00:00:00.000000000 Z
+ zone: !ruby/object:ActiveSupport::TimeZone
+ name: America/New_York
+ time: 1999-12-31 19:00:00.000000000 Z
+ EOF
+
+ assert_equal(yaml, @twz.to_yaml)
end
def test_ruby_to_yaml
- assert_match(/---\s*\n:twz: 2000-01-01 00:00:00(\.0+)?\s*Z\n/, {:twz => @twz}.to_yaml)
+ yaml = <<-EOF.strip_heredoc
+ ---
+ twz: !ruby/object:ActiveSupport::TimeWithZone
+ utc: 2000-01-01 00:00:00.000000000 Z
+ zone: !ruby/object:ActiveSupport::TimeZone
+ name: America/New_York
+ time: 1999-12-31 19:00:00.000000000 Z
+ EOF
+
+ assert_equal(yaml, { 'twz' => @twz }.to_yaml)
+ end
+
+ def test_yaml_load
+ yaml = <<-EOF.strip_heredoc
+ --- !ruby/object:ActiveSupport::TimeWithZone
+ utc: 2000-01-01 00:00:00.000000000 Z
+ zone: !ruby/object:ActiveSupport::TimeZone
+ name: America/New_York
+ time: 1999-12-31 19:00:00.000000000 Z
+ EOF
+
+ assert_equal(@twz, YAML.load(yaml))
+ end
+
+ def test_ruby_yaml_load
+ yaml = <<-EOF.strip_heredoc
+ ---
+ twz: !ruby/object:ActiveSupport::TimeWithZone
+ utc: 2000-01-01 00:00:00.000000000 Z
+ zone: !ruby/object:ActiveSupport::TimeZone
+ name: America/New_York
+ time: 1999-12-31 19:00:00.000000000 Z
+ EOF
+
+ assert_equal({ 'twz' => @twz }, YAML.load(yaml))
end
def test_httpdate
diff --git a/activesupport/test/time_zone_test.rb b/activesupport/test/time_zone_test.rb
index 7888b9919b..5e0474f449 100644
--- a/activesupport/test/time_zone_test.rb
+++ b/activesupport/test/time_zone_test.rb
@@ -311,6 +311,80 @@ class TimeZoneTest < ActiveSupport::TestCase
end
end
+ def test_strptime
+ zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
+ twz = zone.strptime('1999-12-31 12:00:00', '%Y-%m-%d %H:%M:%S')
+ assert_equal Time.utc(1999,12,31,17), twz
+ assert_equal Time.utc(1999,12,31,12), twz.time
+ assert_equal Time.utc(1999,12,31,17), twz.utc
+ assert_equal zone, twz.time_zone
+ end
+
+ def test_strptime_with_nondefault_time_zone
+ with_tz_default ActiveSupport::TimeZone['Pacific Time (US & Canada)'] do
+ zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
+ twz = zone.strptime('1999-12-31 12:00:00', '%Y-%m-%d %H:%M:%S')
+ assert_equal Time.utc(1999,12,31,17), twz
+ assert_equal Time.utc(1999,12,31,12), twz.time
+ assert_equal Time.utc(1999,12,31,17), twz.utc
+ assert_equal zone, twz.time_zone
+ end
+ end
+
+ def test_strptime_with_explicit_time_zone_as_abbrev
+ zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
+ twz = zone.strptime('1999-12-31 12:00:00 PST', '%Y-%m-%d %H:%M:%S %Z')
+ assert_equal Time.utc(1999,12,31,20), twz
+ assert_equal Time.utc(1999,12,31,15), twz.time
+ assert_equal Time.utc(1999,12,31,20), twz.utc
+ assert_equal zone, twz.time_zone
+ end
+
+ def test_strptime_with_explicit_time_zone_as_h_offset
+ zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
+ twz = zone.strptime('1999-12-31 12:00:00 -08', '%Y-%m-%d %H:%M:%S %:::z')
+ assert_equal Time.utc(1999,12,31,20), twz
+ assert_equal Time.utc(1999,12,31,15), twz.time
+ assert_equal Time.utc(1999,12,31,20), twz.utc
+ assert_equal zone, twz.time_zone
+ end
+
+ def test_strptime_with_explicit_time_zone_as_hm_offset
+ zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
+ twz = zone.strptime('1999-12-31 12:00:00 -08:00', '%Y-%m-%d %H:%M:%S %:z')
+ assert_equal Time.utc(1999,12,31,20), twz
+ assert_equal Time.utc(1999,12,31,15), twz.time
+ assert_equal Time.utc(1999,12,31,20), twz.utc
+ assert_equal zone, twz.time_zone
+ end
+
+ def test_strptime_with_explicit_time_zone_as_hms_offset
+ zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
+ twz = zone.strptime('1999-12-31 12:00:00 -08:00:00', '%Y-%m-%d %H:%M:%S %::z')
+ assert_equal Time.utc(1999,12,31,20), twz
+ assert_equal Time.utc(1999,12,31,15), twz.time
+ assert_equal Time.utc(1999,12,31,20), twz.utc
+ assert_equal zone, twz.time_zone
+ end
+
+ def test_strptime_with_almost_explicit_time_zone
+ zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
+ twz = zone.strptime('1999-12-31 12:00:00 %Z', '%Y-%m-%d %H:%M:%S %%Z')
+ assert_equal Time.utc(1999,12,31,17), twz
+ assert_equal Time.utc(1999,12,31,12), twz.time
+ assert_equal Time.utc(1999,12,31,17), twz.utc
+ assert_equal zone, twz.time_zone
+ end
+
+ def test_strptime_with_day_omitted
+ with_env_tz 'US/Eastern' do
+ zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
+ assert_equal Time.local(2000, 2, 1), zone.strptime('Feb', '%b', Time.local(2000, 1, 1))
+ assert_equal Time.local(2005, 2, 1), zone.strptime('Feb 2005', '%b %Y', Time.local(2000, 1, 1))
+ assert_equal Time.local(2005, 2, 2), zone.strptime('2 Feb 2005', '%e %b %Y', Time.local(2000, 1, 1))
+ end
+ end
+
def test_utc_offset_lazy_loaded_from_tzinfo_when_not_passed_in_to_initialize
tzinfo = TZInfo::Timezone.get('America/New_York')
zone = ActiveSupport::TimeZone.create(tzinfo.name, nil, tzinfo)
@@ -413,4 +487,13 @@ class TimeZoneTest < ActiveSupport::TestCase
assert ActiveSupport::TimeZone.us_zones.include?(ActiveSupport::TimeZone["Hawaii"])
assert !ActiveSupport::TimeZone.us_zones.include?(ActiveSupport::TimeZone["Kuala Lumpur"])
end
+
+ def test_to_yaml
+ assert_equal("--- !ruby/object:ActiveSupport::TimeZone\nname: Pacific/Honolulu\n", ActiveSupport::TimeZone["Hawaii"].to_yaml)
+ assert_equal("--- !ruby/object:ActiveSupport::TimeZone\nname: Europe/London\n", ActiveSupport::TimeZone["Europe/London"].to_yaml)
+ end
+
+ def test_yaml_load
+ assert_equal(ActiveSupport::TimeZone["Pacific/Honolulu"], YAML.load("--- !ruby/object:ActiveSupport::TimeZone\nname: Pacific/Honolulu\n"))
+ end
end
diff --git a/guides/source/4_2_release_notes.md b/guides/source/4_2_release_notes.md
index d5b3766a5b..684bd286bc 100644
--- a/guides/source/4_2_release_notes.md
+++ b/guides/source/4_2_release_notes.md
@@ -214,8 +214,9 @@ end
Due to a [change in Rack](https://github.com/rack/rack/commit/28b014484a8ac0bbb388e7eaeeef159598ec64fc),
`rails server` now listens on `localhost` instead of `0.0.0.0` by default. This
-should have minimal impact on the standard development workflow as http://localhost:3000
-will continue to work as before on your own machine.
+should have minimal impact on the standard development workflow as both
+http://127.0.0.1:3000 and http://localhost:3000 will continue to work as before
+on your own machine.
However, with this change you will no longer be able to access the Rails
server from a different machine, for example if your development environment
diff --git a/guides/source/action_mailer_basics.md b/guides/source/action_mailer_basics.md
index 73b240ff2c..089ce53f07 100644
--- a/guides/source/action_mailer_basics.md
+++ b/guides/source/action_mailer_basics.md
@@ -530,6 +530,9 @@ url helper.
<%= user_url(@user, host: 'example.com') %>
```
+NOTE: non-`GET` links require [jQuery UJS](https://github.com/rails/jquery-ujs)
+and won't work in mailer templates. They will result in normal `GET` requests.
+
### Sending Multipart Emails
Action Mailer will automatically send multipart emails if you have different
diff --git a/guides/source/action_view_overview.md b/guides/source/action_view_overview.md
index 44c02165db..3541bbaa93 100644
--- a/guides/source/action_view_overview.md
+++ b/guides/source/action_view_overview.md
@@ -356,39 +356,34 @@ Supposing we use the same `_box` partial from above, this would produce the same
View Paths
----------
-When rendering the view for a request, the controller needs to resolve where to find each of the directories are located.
+When rendering a response, the controller needs to resolve where the different
+views are located. By default it only looks inside the `app/views` directory.
-We are able to modify the order these locations are resolved by using `prepend_view_path` and `append_view_path`.
-
-This allows us to add new paths to the beginning or end of the list used to resolve these paths.
+We can add other locations and give them a certain precedence when resolving
+paths using the `prepend_view_path` and `append_view_path` methods.
### Prepend view path
-This can be helpful for example, when we want to prepend a different directory for subdomains.
+This can be helpful for example, when we want to put views inside a different
+directory for subdomains.
We can do this by using:
-```prepend_view_path "app/views/#{request.subdomain}"```
-
-Then our list becomes something like:
-
-```
-[
- ~/rails_app/app/views/<subdomain>,
- ~/rails_app/app/views,
- # ...
-]
+```ruby
+prepend_view_path "app/views/#{request.subdomain}"
```
-This will put the subdomain path at the beginning of the list.
+Then Action View will look first in this directory when resolving views.
### Append view path
Similarly, we can append paths:
-```append_view_path "app/views/direct"```.
+```ruby
+append_view_path "app/views/direct"
+```
-This will add ```app/views/direct``` and the end of lookup paths for views.
+This will add `app/views/direct` to the end of the lookup paths.
Overview of helpers provided by Action View
-------------------------------------------
@@ -498,7 +493,7 @@ Returns a stylesheet link tag for the sources specified as arguments. If you don
stylesheet_link_tag "application" # => <link href="/assets/application.css" media="screen" rel="stylesheet" />
```
-You can also include all styles in the stylesheet directory using :all as the source:
+You can also include all styles in the stylesheet directory using `:all` as the source:
```ruby
stylesheet_link_tag :all
@@ -513,7 +508,7 @@ stylesheet_link_tag :all, cache: true
#### stylesheet_path
-Computes the path to a stylesheet asset in the `app/assets/stylesheets` directory. If the source filename has no extension, .css will be appended. Full paths from the document root will be passed through. Used internally by stylesheet_link_tag to build the stylesheet path.
+Computes the path to a stylesheet asset in the `app/assets/stylesheets` directory. If the source filename has no extension, `.css` will be appended. Full paths from the document root will be passed through. Used internally by stylesheet_link_tag to build the stylesheet path.
```ruby
stylesheet_path "application" # => /assets/application.css
@@ -814,9 +809,9 @@ third:
Form helpers are designed to make working with models much easier compared to using just standard HTML elements by providing a set of methods for creating forms based on your models. This helper generates the HTML for forms, providing a method for each sort of input (e.g., text, password, select, and so on). When the form is submitted (i.e., when the user hits the submit button or form.submit is called via JavaScript), the form inputs will be bundled into the params object and passed back to the controller.
-There are two types of form helpers: those that specifically work with model attributes and those that don't. This helper deals with those that work with model attributes; to see an example of form helpers that don't work with model attributes, check the ActionView::Helpers::FormTagHelper documentation.
+There are two types of form helpers: those that specifically work with model attributes and those that don't. This helper deals with those that work with model attributes; to see an example of form helpers that don't work with model attributes, check the `ActionView::Helpers::FormTagHelper` documentation.
-The core method of this helper, form_for, gives you the ability to create a form for a model instance; for example, let's say that you have a model Person and want to create a new instance of it:
+The core method of this helper, `form_for`, gives you the ability to create a form for a model instance; for example, let's say that you have a model Person and want to create a new instance of it:
```html+erb
# Note: a @person variable will have been created in the controller (e.g. @person = Person.new)
@@ -858,7 +853,7 @@ check_box("article", "validated")
#### fields_for
-Creates a scope around a specific model object like form_for, but doesn't create the form tags themselves. This makes fields_for suitable for specifying additional model objects in the same form:
+Creates a scope around a specific model object like `form_for`, but doesn't create the form tags themselves. This makes `fields_for` suitable for specifying additional model objects in the same form:
```html+erb
<%= form_for @person, url: { action: "update" } do |person_form| %>
@@ -1152,7 +1147,7 @@ Returns a string of option tags that have been compiled by iterating over the `c
# options_from_collection_for_select(collection, value_method, text_method, selected = nil)
```
-For example, imagine a loop iterating over each person in @project.people to generate an input tag:
+For example, imagine a loop iterating over each person in `@project.people` to generate an input tag:
```ruby
options_from_collection_for_select(@project.people, "id", "name")
@@ -1453,7 +1448,7 @@ This sanitize helper will HTML encode all tags and strip all attributes that are
sanitize @article.body
```
-If either the :attributes or :tags options are passed, only the mentioned tags and attributes are allowed and nothing else.
+If either the `:attributes` or `:tags` options are passed, only the mentioned tags and attributes are allowed and nothing else.
```ruby
sanitize @article.body, tags: %w(table tr td), attributes: %w(id class style)
diff --git a/guides/source/active_record_querying.md b/guides/source/active_record_querying.md
index de976acd01..2f10bc4e7c 100644
--- a/guides/source/active_record_querying.md
+++ b/guides/source/active_record_querying.md
@@ -317,7 +317,7 @@ end
The `find_each` method accepts most of the options allowed by the regular `find` method, except for `:order` and `:limit`, which are reserved for internal use by `find_each`.
-Two additional options, `:batch_size` and `:begin_at`, are available as well.
+Three additional options, `:batch_size`, `:begin_at` and `:end_at`, are available as well.
**`:batch_size`**
@@ -348,7 +348,7 @@ Another example would be if you wanted multiple workers handling the same proces
Similar to the `:begin_at` option, `:end_at` allows you to configure the last ID of the sequence whenever the highest ID is not the one you need.
This would be useful, for example, if you wanted to run a batch process, using a subset of records based on `:begin_at` and `:end_at`
-For example, to send newsletters only to users with the primary key starting from 2000 up to 10000 and to retrieve them in batches of 1000:
+For example, to send newsletters only to users with the primary key starting from 2000 up to 10000 and to retrieve them in batches of 5000:
```ruby
User.find_each(begin_at: 2000, end_at: 10000, batch_size: 5000) do |user|
diff --git a/guides/source/api_documentation_guidelines.md b/guides/source/api_documentation_guidelines.md
index b385bdbe83..46c9013087 100644
--- a/guides/source/api_documentation_guidelines.md
+++ b/guides/source/api_documentation_guidelines.md
@@ -16,7 +16,8 @@ RDoc
----
The [Rails API documentation](http://api.rubyonrails.org) is generated with
-[RDoc](http://docs.seattlerb.org/rdoc/).
+[RDoc](http://docs.seattlerb.org/rdoc/). To generate it, make sure you are
+in the rails root directory, run `bundle install` and execute:
```bash
bundle exec rake rdoc
diff --git a/guides/source/caching_with_rails.md b/guides/source/caching_with_rails.md
index 716beb9178..782406659d 100644
--- a/guides/source/caching_with_rails.md
+++ b/guides/source/caching_with_rails.md
@@ -22,7 +22,7 @@ fragment caching. Rails provides by default fragment caching. In order to use
page and action caching, you will need to add `actionpack-page_caching` and
`actionpack-action_caching` to your Gemfile.
-To start playing with caching you'll want to ensure that `config.action_controller.perform_caching` is set to `true`, if you're running in development mode. This flag is normally set in the corresponding `config/environments/*.rb` and caching is disabled by default for development and test, and enabled for production.
+To start playing with caching you'll want to ensure that `config.action_controller.perform_caching` is set to `true` if you're running in development mode. This flag is normally set in the corresponding `config/environments/*.rb` and caching is disabled by default for development and test, and enabled for production.
```ruby
config.action_controller.perform_caching = true
@@ -144,7 +144,7 @@ It's called "Russian Doll Caching" because it nests multiple fragments. The adva
### Low-Level Caching
-Sometimes you need to cache a particular value or query result, instead of caching view fragments. Rails caching mechanism works great for storing __any__ kind of information.
+Sometimes you need to cache a particular value or query result instead of caching view fragments. Rails' caching mechanism works great for storing __any__ kind of information.
The most efficient way to implement low-level caching is using the `Rails.cache.fetch` method. This method does both reading and writing to the cache. When passed only a single argument, the key is fetched and value from the cache is returned. If a block is passed, the result of the block will be cached to the given key and the result is returned.
@@ -160,7 +160,7 @@ class Product < ActiveRecord::Base
end
```
-NOTE: Notice that in this example we used `cache_key` method, so the resulting cache-key will be something like `products/233-20140225082222765838000/competing_price`. `cache_key` generates a string based on the model’s `id` and `updated_at` attributes. This is a common convention and has the benefit of invalidating the cache whenever the product is updated. In general, when you use low-level caching for instance level information, you need to generate a cache key.
+NOTE: Notice that in this example we used the `cache_key` method, so the resulting cache-key will be something like `products/233-20140225082222765838000/competing_price`. `cache_key` generates a string based on the model’s `id` and `updated_at` attributes. This is a common convention and has the benefit of invalidating the cache whenever the product is updated. In general, when you use low-level caching for instance level information, you need to generate a cache key.
### SQL Caching
@@ -219,7 +219,7 @@ There are some common options used by all cache implementations. These can be pa
* `:compress` - This option can be used to indicate that compression should be used in the cache. This can be useful for transferring large cache entries over a slow network.
-* `:compress_threshold` - This options is used in conjunction with the `:compress` option to indicate a threshold under which cache entries should not be compressed. This defaults to 16 kilobytes.
+* `:compress_threshold` - This option is used in conjunction with the `:compress` option to indicate a threshold under which cache entries should not be compressed. This defaults to 16 kilobytes.
* `:expires_in` - This option sets an expiration time in seconds for the cache entry when it will be automatically removed from the cache.
@@ -227,7 +227,7 @@ There are some common options used by all cache implementations. These can be pa
### ActiveSupport::Cache::MemoryStore
-This cache store keeps entries in memory in the same Ruby process. The cache store has a bounded size specified by the `:size` options to the initializer (default is 32Mb). When the cache exceeds the allotted size, a cleanup will occur and the least recently used entries will be removed.
+This cache store keeps entries in memory in the same Ruby process. The cache store has a bounded size specified by the `:size` option to the initializer (default is 32Mb). When the cache exceeds the allotted size, a cleanup will occur and the least recently used entries will be removed.
```ruby
config.cache_store = :memory_store, { size: 64.megabytes }
@@ -243,7 +243,7 @@ This cache store uses the file system to store entries. The path to the director
config.cache_store = :file_store, "/path/to/cache/directory"
```
-With this cache store, multiple server processes on the same host can share a cache. Servers processes running on different hosts could share a cache by using a shared file system, but that set up would not be ideal and is not recommended. The cache store is appropriate for low to medium traffic sites that are served off one or two hosts.
+With this cache store, multiple server processes on the same host can share a cache. Server processes running on different hosts could share a cache by using a shared file system, but that set up would not be ideal and is not recommended. The cache store is appropriate for low to medium traffic sites that are served off one or two hosts.
Note that the cache will grow until the disk is full unless you periodically clear out old entries.
@@ -255,7 +255,7 @@ This cache store uses Danga's `memcached` server to provide a centralized cache
When initializing the cache, you need to specify the addresses for all memcached servers in your cluster. If none is specified, it will assume memcached is running on the local host on the default port, but this is not an ideal set up for larger sites.
-The `write` and `fetch` methods on this cache accept two additional options that take advantage of features specific to memcached. You can specify `:raw` to send a value directly to the server with no serialization. The value must be a string or number. You can use memcached direct operation like `increment` and `decrement` only on raw values. You can also specify `:unless_exist` if you don't want memcached to overwrite an existing entry.
+The `write` and `fetch` methods on this cache accept two additional options that take advantage of features specific to memcached. You can specify `:raw` to send a value directly to the server with no serialization. The value must be a string or number. You can use memcached direct operations like `increment` and `decrement` only on raw values. You can also specify `:unless_exist` if you don't want memcached to overwrite an existing entry.
```ruby
config.cache_store = :mem_cache_store, "cache-1.example.com", "cache-2.example.com"
@@ -293,7 +293,7 @@ For more information about Ehcache for JRuby and Rails, see [http://ehcache.org/
### ActiveSupport::Cache::NullStore
-This cache store implementation is meant to be used only in development or test environments and it never stores anything. This can be very useful in development when you have code that interacts directly with `Rails.cache`, but caching may interfere with being able to see the results of code changes. With this cache store, all `fetch` and `read` operations will result in a miss.
+This cache store implementation is meant to be used only in development or test environments and it never stores anything. This can be very useful in development when you have code that interacts directly with `Rails.cache` but caching may interfere with being able to see the results of code changes. With this cache store, all `fetch` and `read` operations will result in a miss.
```ruby
config.cache_store = :null_store
@@ -303,7 +303,7 @@ config.cache_store = :null_store
You can create your own custom cache store by simply extending `ActiveSupport::Cache::Store` and implementing the appropriate methods. In this way, you can swap in any number of caching technologies into your Rails application.
-To use a custom cache store, simple set the cache store to a new instance of the class.
+To use a custom cache store, simply set the cache store to a new instance of the class.
```ruby
config.cache_store = MyCacheStore.new
@@ -311,7 +311,7 @@ config.cache_store = MyCacheStore.new
### Cache Keys
-The keys used in a cache can be any object that responds to either `:cache_key` or to `:to_param`. You can implement the `:cache_key` method on your classes if you need to generate custom keys. Active Record will generate keys based on the class name and record id.
+The keys used in a cache can be any object that responds to either `:cache_key` or `:to_param`. You can implement the `:cache_key` method on your classes if you need to generate custom keys. Active Record will generate keys based on the class name and record id.
You can use Hashes and Arrays of values as cache keys.
@@ -353,7 +353,7 @@ class ProductsController < ApplicationController
end
```
-Instead of an options hash, you can also simply pass in a model, Rails will use the `updated_at` and `cache_key` methods for setting `last_modified` and `etag`:
+Instead of an options hash, you can also simply pass in a model. Rails will use the `updated_at` and `cache_key` methods for setting `last_modified` and `etag`:
```ruby
class ProductsController < ApplicationController
diff --git a/guides/source/configuring.md b/guides/source/configuring.md
index aa66376d5d..4da369be5e 100644
--- a/guides/source/configuring.md
+++ b/guides/source/configuring.md
@@ -114,7 +114,7 @@ numbers. New applications filter out passwords by adding the following `config.f
defaults to `:debug` for all environments. The available log levels are: `:debug`,
`:info`, `:warn`, `:error`, `:fatal`, and `:unknown`.
-* `config.log_tags` accepts a list of methods that the `request` object responds to. This makes it easy to tag log lines with debug information like subdomain and request id - both very helpful in debugging multi-user production applications.
+* `config.log_tags` accepts a list of: methods that the `request` object responds to, a `Proc` that accepts the `request` object, or something that responds to `to_s`. This makes it easy to tag log lines with debug information like subdomain and request id - both very helpful in debugging multi-user production applications.
* `config.logger` accepts a logger conforming to the interface of Log4r or the default Ruby `Logger` class. Defaults to an instance of `ActiveSupport::Logger`.
@@ -524,7 +524,7 @@ 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.test_order` sets the order that test cases are executed. Possible values are `:sorted` and `:random`. Currently defaults to `:sorted`. In Rails 5.0, the default will be changed to `:random` instead.
+* `config.active_support.test_order` sets the order that test cases are executed. Possible values are `:random` and `:sorted`. This option is set to `:random` in `config/environments/test.rb` in newly-generated applications. If you have an application that does not specify a `test_order`, it will default to `:sorted`, *until* Rails 5.0, when the default will become `:random`.
* `config.active_support.escape_html_entities_in_json` enables or disables the escaping of HTML entities in JSON serialization. Defaults to `false`.
diff --git a/guides/source/contributing_to_ruby_on_rails.md b/guides/source/contributing_to_ruby_on_rails.md
index 7778b6c5e0..3d5f8906ca 100644
--- a/guides/source/contributing_to_ruby_on_rails.md
+++ b/guides/source/contributing_to_ruby_on_rails.md
@@ -403,21 +403,27 @@ When you're happy with the code on your computer, you need to commit the changes
$ git commit -a
```
-At this point, your editor should be fired up and you can write a message for this commit. Well formatted and descriptive commit messages are extremely helpful for the others, especially when figuring out why given change was made, so please take the time to write it.
+This should fire up your editor to write a commit message. When you have
+finished, save and close to continue.
-Good commit message should be formatted according to the following example:
+A well-formatted and descriptive commit message is very helpful to others for
+understanding why the change was made, so please take the time to write it.
+
+A good commit message looks like this:
```
Short summary (ideally 50 characters or less)
-More detailed description, if necessary. It should be wrapped to 72
-characters. Try to be as descriptive as you can; even if you think that the
-commit content is obvious, it may not be obvious to others. Add any description
-that is already present in relevant issues - it should not be necessary to visit
-a webpage to check the history.
+More detailed description, if necessary. It should be wrapped to
+72 characters. Try to be as descriptive as you can. Even if you
+think that the commit content is obvious, it may not be obvious
+to others. Add any description that is already present in the
+relevant issues; it should not be necessary to visit a webpage
+to check the history.
+
+The description section can have multiple paragraphs.
-The description section can have multiple paragraphs. Code examples can be
-embedded by indenting them with 4 spaces:
+Code examples can be embedded by indenting them with 4 spaces:
class ArticlesController
def index
@@ -427,14 +433,15 @@ embedded by indenting them with 4 spaces:
You can also add bullet points:
-- you can use dashes or asterisks
+- make a bullet point by starting a line with either a dash (-)
+ or an asterisk (*)
-- also, try to indent next line of a point for readability, if it's too
- long to fit in 72 characters
+- wrap lines at 72 characters, and indent any additional lines
+ with 2 spaces for readability
```
TIP. Please squash your commits into a single commit when appropriate. This
-simplifies future cherry picks and also keeps the git log clean.
+simplifies future cherry picks and keeps the git log clean.
### Update Your Branch
diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md
index bae2a1bd43..31168ff45e 100644
--- a/guides/source/getting_started.md
+++ b/guides/source/getting_started.md
@@ -23,7 +23,7 @@ application from scratch. It does not assume that you have any prior experience
with Rails. However, to get the most out of it, you need to have some
prerequisites installed:
-* The [Ruby](https://www.ruby-lang.org/en/downloads) language version 1.9.3 or newer.
+* The [Ruby](https://www.ruby-lang.org/en/downloads) language version 2.2.2 or newer.
* The [RubyGems](https://rubygems.org) packaging system, which is installed with Ruby
versions 1.9 and later. To learn more about RubyGems, please read the [RubyGems Guides](http://guides.rubygems.org).
* A working installation of the [SQLite3 Database](https://www.sqlite.org).
@@ -1240,10 +1240,7 @@ article we want to show the form back to the user.
We reuse the `article_params` method that we defined earlier for the create
action.
-TIP: You don't need to pass all attributes to `update`. For
-example, if you'd call `@article.update(title: 'A new title')`
-Rails would only update the `title` attribute, leaving all other
-attributes untouched.
+TIP: It is not necessary to pass all the attributes to `update`. For example, if `@article.update(title: 'A new title')` were called, Rails would only update the `title` attribute, leaving all other attributes untouched.
Finally, we want to show a link to the `edit` action in the list of all the
articles, so let's add that now to `app/views/articles/index.html.erb` to make
diff --git a/guides/source/ruby_on_rails_guides_guidelines.md b/guides/source/ruby_on_rails_guides_guidelines.md
index 1323742488..50866350f8 100644
--- a/guides/source/ruby_on_rails_guides_guidelines.md
+++ b/guides/source/ruby_on_rails_guides_guidelines.md
@@ -64,7 +64,9 @@ The guides and the API should be coherent and consistent where appropriate. In p
HTML Guides
-----------
-Before generating the guides, make sure that you have the latest version of Bundler installed on your system. As of this writing, you must install Bundler 1.3.5 on your device.
+Before generating the guides, make sure that you have the latest version of
+Bundler installed on your system. As of this writing, you must install Bundler
+1.3.5 or later on your device.
To install the latest version of Bundler, run `gem install bundler`.
@@ -82,6 +84,8 @@ or
bundle exec rake guides:generate:html
```
+Resulting HTML files can be found in the `./output` directory.
+
To process `my_guide.md` and nothing else use the `ONLY` environment variable:
```
diff --git a/guides/source/testing.md b/guides/source/testing.md
index f12daf0dbc..cc469f4dae 100644
--- a/guides/source/testing.md
+++ b/guides/source/testing.md
@@ -407,9 +407,49 @@ This test should now pass.
By now you've caught a glimpse of some of the assertions that are available. Assertions are the worker bees of testing. They are the ones that actually perform the checks to ensure that things are going as planned.
-There are a bunch of different types of assertions you can use that come with [`Minitest`](https://github.com/seattlerb/minitest), the default testing library used by Rails.
-
-For a list of all available assertions please check the [Minitest API documentation](http://docs.seattlerb.org/minitest/), specifically [`Minitest::Assertions`](http://docs.seattlerb.org/minitest/Minitest/Assertions.html)
+Here's an extract of the assertions you can use with
+[`Minitest`](https://github.com/seattlerb/minitest), the default testing library
+used by Rails. The `[msg]` parameter is an optional string message you can
+specify to make your test failure messages clearer. It's not required.
+
+| Assertion | Purpose |
+| ---------------------------------------------------------------- | ------- |
+| `assert( test, [msg] )` | Ensures that `test` is true.|
+| `assert_not( test, [msg] )` | Ensures that `test` is false.|
+| `assert_equal( expected, actual, [msg] )` | Ensures that `expected == actual` is true.|
+| `assert_not_equal( expected, actual, [msg] )` | Ensures that `expected != actual` is true.|
+| `assert_same( expected, actual, [msg] )` | Ensures that `expected.equal?(actual)` is true.|
+| `assert_not_same( expected, actual, [msg] )` | Ensures that `expected.equal?(actual)` is false.|
+| `assert_nil( obj, [msg] )` | Ensures that `obj.nil?` is true.|
+| `assert_not_nil( obj, [msg] )` | Ensures that `obj.nil?` is false.|
+| `assert_empty( obj, [msg] )` | Ensures that `obj` is `empty?`.|
+| `assert_not_empty( obj, [msg] )` | Ensures that `obj` is not `empty?`.|
+| `assert_match( regexp, string, [msg] )` | Ensures that a string matches the regular expression.|
+| `assert_no_match( regexp, string, [msg] )` | Ensures that a string doesn't match the regular expression.|
+| `assert_includes( collection, obj, [msg] )` | Ensures that `obj` is in `collection`.|
+| `assert_not_includes( collection, obj, [msg] )` | Ensures that `obj` is not in `collection`.|
+| `assert_in_delta( expecting, actual, [delta], [msg] )` | Ensures that the numbers `expected` and `actual` are within `delta` of each other.|
+| `assert_not_in_delta( expecting, actual, [delta], [msg] )` | Ensures that the numbers `expected` and `actual` are not within `delta` of each other.|
+| `assert_throws( symbol, [msg] ) { block }` | Ensures that the given block throws the symbol.|
+| `assert_raises( exception1, exception2, ... ) { block }` | Ensures that the given block raises one of the given exceptions.|
+| `assert_nothing_raised( exception1, exception2, ... ) { block }` | Ensures that the given block doesn't raise one of the given exceptions.|
+| `assert_instance_of( class, obj, [msg] )` | Ensures that `obj` is an instance of `class`.|
+| `assert_not_instance_of( class, obj, [msg] )` | Ensures that `obj` is not an instance of `class`.|
+| `assert_kind_of( class, obj, [msg] )` | Ensures that `obj` is or descends from `class`.|
+| `assert_not_kind_of( class, obj, [msg] )` | Ensures that `obj` is not an instance of `class` and is not descending from it.|
+| `assert_respond_to( obj, symbol, [msg] )` | Ensures that `obj` responds to `symbol`.|
+| `assert_not_respond_to( obj, symbol, [msg] )` | Ensures that `obj` does not respond to `symbol`.|
+| `assert_operator( obj1, operator, [obj2], [msg] )` | Ensures that `obj1.operator(obj2)` is true.|
+| `assert_not_operator( obj1, operator, [obj2], [msg] )` | Ensures that `obj1.operator(obj2)` is false.|
+| `assert_predicate ( obj, predicate, [msg] )` | Ensures that `obj.predicate` is true, e.g. `assert_predicate str, :empty?`|
+| `assert_not_predicate ( obj, predicate, [msg] )` | Ensures that `obj.predicate` is false, e.g. `assert_not_predicate str, :empty?`|
+| `assert_send( array, [msg] )` | Ensures that executing the method listed in `array[1]` on the object in `array[0]` with the parameters of `array[2 and up]` is true. This one is weird eh?|
+| `flunk( [msg] )` | Ensures failure. This is useful to explicitly mark a test that isn't finished yet.|
+
+The above are a subset of assertions that minitest supports. For an exhaustive &
+more up-to-date list, please check
+[Minitest API documentation](http://docs.seattlerb.org/minitest/), specifically
+[`Minitest::Assertions`](http://docs.seattlerb.org/minitest/Minitest/Assertions.html)
Because of the modular nature of the testing framework, it is possible to create your own assertions. In fact, that's exactly what Rails does. It includes some specialized assertions to make your life easier.
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index 6193c33593..fb3953f866 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,4 +1,9 @@
-* Rename `railties/bin` to `railties/exe` to match the new Bundler executables convention.
+* Remove sqlite support from `rails dbconsole`.
+
+ *Andrew White*
+
+* Rename `railties/bin` to `railties/exe` to match the new Bundler executables
+ convention.
*Islam Wazery*
@@ -22,7 +27,7 @@
*arthurnn*
-* Add `rake initializers`
+* Add `rake initializers`.
This task prints out all defined initializers in the order they are invoked
by Rails. This is helpful for debugging issues related to the initialization
@@ -37,7 +42,7 @@
*Hyonjee Joo*
-* Add `config/initializers/active_record_belongs_to_required_by_default.rb`
+* Add `config/initializers/active_record_belongs_to_required_by_default.rb`.
Newly generated Rails apps have a new initializer called
`active_record_belongs_to_required_by_default.rb` which sets the value of
@@ -56,7 +61,7 @@
'No such middleware' errors when `insert_before` or `insert_after` are added
after the `delete` operation for the middleware being deleted.
- Fixes: #16433.
+ Fixes #16433.
*Guo Xiang Tan*
@@ -68,11 +73,11 @@
*Xavier Noria*
-* Force generated routes to be inserted into routes.rb
+* Force generated routes to be inserted into `config/routes.rb`.
*Andrew White*
-* Don't remove all line endings from routes.rb when revoking scaffold.
+* Don't remove all line endings from `config/routes.rb` when revoking scaffold.
Fixes #15913.
@@ -82,11 +87,12 @@
*Melanie Gilman*
-* Add the `method_source` gem to the default Gemfile for apps
+* Add the `method_source` gem to the default Gemfile for apps.
*Sean Griffin*
-* Drop old test locations from `rake stats`
+* Drop old test locations from `rake stats`:
+
- test/functional
- test/unit
@@ -117,7 +123,7 @@
*Andrew Kozlov*
-* Add `config/initializers/callback_terminator.rb`
+* Add `config/initializers/callback_terminator.rb`.
Newly generated Rails apps have a new initializer called
`callback_terminator.rb` which sets the value of the configuration option
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index 3e3ef72742..a65f8f2ad9 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -1,4 +1,5 @@
require 'fileutils'
+require 'yaml'
require 'active_support/core_ext/hash/keys'
require 'active_support/core_ext/object/blank'
require 'active_support/key_generator'
@@ -228,7 +229,6 @@ module Rails
yaml = Pathname.new("#{paths["config"].existent.first}/#{name}.yml")
if yaml.exist?
- require "yaml"
require "erb"
(YAML.load(ERB.new(yaml.read).result) || {})[Rails.env] || {}
else
diff --git a/railties/lib/rails/commands/dbconsole.rb b/railties/lib/rails/commands/dbconsole.rb
index 5175e31f14..3b22b582cf 100644
--- a/railties/lib/rails/commands/dbconsole.rb
+++ b/railties/lib/rails/commands/dbconsole.rb
@@ -50,9 +50,6 @@ module Rails
ENV['PGPASSWORD'] = config["password"].to_s if config["password"] && options['include_password']
find_cmd_and_exec('psql', config["database"])
- when "sqlite"
- find_cmd_and_exec('sqlite', config["database"])
-
when "sqlite3"
args = []
@@ -89,7 +86,7 @@ module Rails
find_cmd_and_exec("sqsh", *args)
else
- abort "Unknown command-line client for #{config['database']}. Submit a Rails patch to add support!"
+ abort "Unknown command-line client for #{config['database']}."
end
end
diff --git a/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb
index d9713b0238..d99b27cb2d 100644
--- a/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb
+++ b/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb
@@ -17,6 +17,7 @@
<%%= f.label :password %><br>
<%%= f.password_field :password %>
</div>
+
<div class="field">
<%%= f.label :password_confirmation %><br>
<%%= f.password_field :password_confirmation %>
@@ -25,6 +26,7 @@
<%%= f.<%= attribute.field_type %> :<%= attribute.column_name %> %>
<% end -%>
</div>
+
<% end -%>
<div class="actions">
<%%= f.submit %>
diff --git a/railties/lib/rails/generators/rails/controller/controller_generator.rb b/railties/lib/rails/generators/rails/controller/controller_generator.rb
index 6c583e5811..0a4c509a31 100644
--- a/railties/lib/rails/generators/rails/controller/controller_generator.rb
+++ b/railties/lib/rails/generators/rails/controller/controller_generator.rb
@@ -19,7 +19,8 @@ module Rails
end
end
- hook_for :template_engine, :test_framework, :helper, :assets
+ hook_for :template_engine, :test_framework
+ hook_for :helper, :assets, hide: true
private
diff --git a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb
index 68c3829515..fe19fa38d7 100644
--- a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb
+++ b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb
@@ -8,7 +8,7 @@ module Rails
# generator.
#
# This allows you to override entire operations, like the creation of the
- # Gemfile, README, or JavaScript files, without needing to know exactly
+ # Gemfile, \README, or JavaScript files, without needing to know exactly
# what those operations do so you can create another template action.
class PluginBuilder
def rakefile
diff --git a/railties/lib/rails/generators/rails/scaffold/templates/scaffold.css b/railties/lib/rails/generators/rails/scaffold/templates/scaffold.css
index 69af1e8307..b7818883d1 100644
--- a/railties/lib/rails/generators/rails/scaffold/templates/scaffold.css
+++ b/railties/lib/rails/generators/rails/scaffold/templates/scaffold.css
@@ -1,8 +1,11 @@
-body { background-color: #fff; color: #333; }
+body {
+ background-color: #fff;
+ color: #333;
+}
body, p, ol, ul, td {
font-family: verdana, arial, helvetica, sans-serif;
- font-size: 13px;
+ font-size: 13px;
line-height: 18px;
margin: 33px;
}
@@ -13,9 +16,18 @@ pre {
font-size: 11px;
}
-a { color: #000; }
-a:visited { color: #666; }
-a:hover { color: #fff; background-color:#000; }
+a {
+ color: #000;
+}
+
+a:visited {
+ color: #666;
+}
+
+a:hover {
+ color: #fff;
+ background-color: #000;
+}
th {
padding-bottom: 5px;
@@ -27,7 +39,8 @@ td {
padding-right: 5px;
}
-div.field, div.actions {
+div.field,
+div.actions {
margin-bottom: 10px;
}
@@ -56,7 +69,7 @@ div.field, div.actions {
padding: 5px 5px 5px 15px;
font-size: 12px;
margin: -7px;
- margin-bottom: 0px;
+ margin-bottom: 0;
background-color: #c00;
color: #fff;
}
diff --git a/railties/lib/rails/mailers_controller.rb b/railties/lib/rails/mailers_controller.rb
index 32740d66da..cd1c097eb6 100644
--- a/railties/lib/rails/mailers_controller.rb
+++ b/railties/lib/rails/mailers_controller.rb
@@ -16,10 +16,10 @@ class Rails::MailersController < Rails::ApplicationController # :nodoc:
@page_title = "Mailer Previews for #{@preview.preview_name}"
render action: 'mailer'
else
- email = File.basename(params[:path])
+ @email_action = File.basename(params[:path])
- if @preview.email_exists?(email)
- @email = @preview.call(email)
+ if @preview.email_exists?(@email_action)
+ @email = @preview.call(@email_action)
if params[:part]
part_type = Mime::Type.lookup(params[:part])
@@ -28,14 +28,14 @@ class Rails::MailersController < Rails::ApplicationController # :nodoc:
response.content_type = part_type
render text: part.respond_to?(:decoded) ? part.decoded : part
else
- raise AbstractController::ActionNotFound, "Email part '#{part_type}' not found in #{@preview.name}##{email}"
+ raise AbstractController::ActionNotFound, "Email part '#{part_type}' not found in #{@preview.name}##{@email_action}"
end
else
@part = find_preferred_part(request.format, Mime::HTML, Mime::TEXT)
render action: 'email', layout: false, formats: %w[html]
end
else
- raise AbstractController::ActionNotFound, "Email '#{email}' not found in #{@preview.name}"
+ raise AbstractController::ActionNotFound, "Email '#{@email_action}' not found in #{@preview.name}"
end
end
end
diff --git a/railties/lib/rails/rack/logger.rb b/railties/lib/rails/rack/logger.rb
index 9962e6d943..7aaa353b91 100644
--- a/railties/lib/rails/rack/logger.rb
+++ b/railties/lib/rails/rack/logger.rb
@@ -7,6 +7,9 @@ require 'rack/body_proxy'
module Rails
module Rack
# Sets log tags, logs the request, calls the app, and flushes the logs.
+ #
+ # Log tags (+taggers+) can be an Array containing: methods that the <tt>request</tt> object responds to, a Proc
+ # that accepts an instance of the <tt>request</tt> object, or something that responds to <tt>to_s</tt>.
class Logger < ActiveSupport::LogSubscriber
def initialize(app, taggers = nil)
@app = app
diff --git a/railties/lib/rails/templates/rails/mailers/email.html.erb b/railties/lib/rails/templates/rails/mailers/email.html.erb
index 0b08a01896..afca6368d5 100644
--- a/railties/lib/rails/templates/rails/mailers/email.html.erb
+++ b/railties/lib/rails/templates/rails/mailers/email.html.erb
@@ -39,6 +39,10 @@
padding: 1px;
}
+ dd:empty:before {
+ content: "\00a0"; // &nbsp;
+ }
+
iframe {
border: 0;
width: 100%;
@@ -99,7 +103,14 @@
</dl>
</header>
-<iframe seamless name="messageBody" src="?part=<%= Rack::Utils.escape(@part.mime_type) %>"></iframe>
+<% if @part.mime_type %>
+ <iframe seamless name="messageBody" src="?part=<%= Rack::Utils.escape(@part.mime_type) %>"></iframe>
+<% else %>
+ <p>
+ You are trying to preview an email that does not have any content.
+ This is probably because the <em>mail</em> method has not been called in <em><%= @preview.preview_name %>#<%= @email_action %></em>.
+ </p>
+<% end %>
</body>
</html>
diff --git a/railties/lib/rails/templates/rails/welcome/index.html.erb b/railties/lib/rails/templates/rails/welcome/index.html.erb
index 6726c23fc9..acf04af416 100644
--- a/railties/lib/rails/templates/rails/welcome/index.html.erb
+++ b/railties/lib/rails/templates/rails/welcome/index.html.erb
@@ -18,7 +18,9 @@
color: #000;
}
- a {color: #03c}
+ a {
+ color: #03c;
+ }
a:hover {
background-color: #03c;
@@ -64,7 +66,10 @@
height: 64px;
}
- #header h1, #header h2 {margin: 0}
+ #header h1,
+ #header h2 {
+ margin: 0;
+ }
#header h2 {
color: #888;
@@ -104,7 +109,9 @@
color: #555;
}
- #about-content td.value {color: #000}
+ #about-content td.value {
+ color: #000;
+ }
#about-content ul {
padding: 0;
diff --git a/railties/test/application/assets_test.rb b/railties/test/application/assets_test.rb
index f6b7d4c855..1e2a9ba040 100644
--- a/railties/test/application/assets_test.rb
+++ b/railties/test/application/assets_test.rb
@@ -191,6 +191,7 @@ module ApplicationTests
test "asset pipeline should use a Sprockets::Index when config.assets.digest is true" do
add_to_config "config.action_controller.perform_caching = false"
+ add_to_env_config "production", "config.assets.compile = true"
ENV["RAILS_ENV"] = "production"
require "#{app_path}/config/environment"
@@ -464,17 +465,6 @@ module ApplicationTests
assert_match "src='/sub/uri/assets/rails.png'", File.read(Dir["#{app_path}/public/assets/app-*.js"].first)
end
- test "assets:cache:clean should clean cache" do
- ENV["RAILS_ENV"] = "production"
- precompile!
-
- quietly do
- Dir.chdir(app_path){ `bundle exec rake assets:clobber` }
- end
-
- assert !File.exist?("#{app_path}/tmp/cache/assets")
- end
-
private
def app_with_assets_in_view
diff --git a/railties/test/application/mailer_previews_test.rb b/railties/test/application/mailer_previews_test.rb
index 1752a9f3c6..83501a7f11 100644
--- a/railties/test/application/mailer_previews_test.rb
+++ b/railties/test/application/mailer_previews_test.rb
@@ -327,6 +327,32 @@ module ApplicationTests
assert_match "Email &#39;bar&#39; not found in NotifierPreview", last_response.body
end
+ test "mailer preview NullMail" do
+ mailer 'notifier', <<-RUBY
+ class Notifier < ActionMailer::Base
+ default from: "from@example.com"
+
+ def foo
+ # does not call +mail+
+ end
+ end
+ RUBY
+
+ mailer_preview 'notifier', <<-RUBY
+ class NotifierPreview < ActionMailer::Preview
+ def foo
+ Notifier.foo
+ end
+ end
+ RUBY
+
+ app('development')
+
+ get "/rails/mailers/notifier/foo"
+ assert_match "You are trying to preview an email that does not have any content.", last_response.body
+ assert_match "notifier#foo", last_response.body
+ end
+
test "mailer preview email part not found" do
mailer 'notifier', <<-RUBY
class Notifier < ActionMailer::Base
diff --git a/railties/test/application/rake/dbs_test.rb b/railties/test/application/rake/dbs_test.rb
index c414732f92..5cc9790b28 100644
--- a/railties/test/application/rake/dbs_test.rb
+++ b/railties/test/application/rake/dbs_test.rb
@@ -181,6 +181,34 @@ module ApplicationTests
end
end
+ test "db:schema:load with inflections" do
+ Dir.chdir(app_path) do
+ app_file 'config/initializers/inflection.rb', <<-RUBY
+ ActiveSupport::Inflector.inflections do |inflect|
+ inflect.irregular 'goose', 'geese'
+ end
+ RUBY
+ app_file 'config/initializers/primary_key_table_name.rb', <<-RUBY
+ ActiveRecord::Base.primary_key_prefix_type = :table_name
+ RUBY
+ app_file 'db/schema.rb', <<-RUBY
+ ActiveRecord::Schema.define(version: 20140423102712) do
+ create_table("goose".pluralize) do |t|
+ t.string :name
+ end
+ end
+ RUBY
+
+ `bin/rake db:schema:load`
+
+ tables = `bin/rails runner 'p ActiveRecord::Base.connection.tables'`.strip
+ assert_match(/"geese"/, tables)
+
+ columns = `bin/rails runner 'p ActiveRecord::Base.connection.columns("geese").map(&:name)'`.strip
+ assert_equal columns, '["gooseid", "name"]'
+ end
+ end
+
def db_test_load_structure
Dir.chdir(app_path) do
`rails generate model book title:string;
diff --git a/railties/test/commands/dbconsole_test.rb b/railties/test/commands/dbconsole_test.rb
index a3cd1eb0ed..2e8dd99f36 100644
--- a/railties/test/commands/dbconsole_test.rb
+++ b/railties/test/commands/dbconsole_test.rb
@@ -158,12 +158,6 @@ class Rails::DBConsoleTest < ActiveSupport::TestCase
assert_equal 'q1w2e3', ENV['PGPASSWORD']
end
- def test_sqlite
- start(adapter: 'sqlite', database: 'db')
- assert !aborted
- assert_equal ['sqlite', 'db'], dbconsole.find_cmd_and_exec_args
- end
-
def test_sqlite3
start(adapter: 'sqlite3', database: 'db.sqlite3')
assert !aborted
diff --git a/railties/test/generators/named_base_test.rb b/railties/test/generators/named_base_test.rb
index 18a26fde05..1c32fc1bfd 100644
--- a/railties/test/generators/named_base_test.rb
+++ b/railties/test/generators/named_base_test.rb
@@ -1,6 +1,6 @@
require 'generators/generators_test_helper'
require 'rails/generators/rails/scaffold_controller/scaffold_controller_generator'
-require 'mocha/setup' # FIXME: stop using mocha
+require 'minitest/mock'
class NamedBaseTest < Rails::Generators::TestCase
include GeneratorsTestHelper
@@ -80,10 +80,13 @@ class NamedBaseTest < Rails::Generators::TestCase
def test_application_name
g = generator ['Admin::Foo']
- Rails.stubs(:application).returns(Object.new)
- assert_name g, "object", :application_name
- Rails.stubs(:application).returns(nil)
- assert_name g, "application", :application_name
+ Rails.stub(:application, Object.new) do
+ assert_name g, "object", :application_name
+ end
+
+ Rails.stub(:application, nil) do
+ assert_name g, "application", :application_name
+ end
end
def test_index_helper
@@ -103,11 +106,11 @@ class NamedBaseTest < Rails::Generators::TestCase
def test_hide_namespace
g = generator ['Hidden']
- g.class.stubs(:namespace).returns('hidden')
-
- assert !Rails::Generators.hidden_namespaces.include?('hidden')
- g.class.hide!
- assert Rails::Generators.hidden_namespaces.include?('hidden')
+ g.class.stub(:namespace, 'hidden') do
+ assert !Rails::Generators.hidden_namespaces.include?('hidden')
+ g.class.hide!
+ assert Rails::Generators.hidden_namespaces.include?('hidden')
+ end
end
def test_scaffold_plural_names_with_model_name_option
diff --git a/railties/test/generators_test.rb b/railties/test/generators_test.rb
index 127e059bf1..fdcfb30090 100644
--- a/railties/test/generators_test.rb
+++ b/railties/test/generators_test.rb
@@ -34,6 +34,12 @@ class GeneratorsTest < Rails::Generators::TestCase
assert_match "Maybe you meant 'migration'", output
end
+ def test_generator_multiple_suggestions
+ name = :tas
+ output = capture(:stdout){ Rails::Generators.invoke name }
+ assert_match "Maybe you meant 'task', 'job' or 'assets'", output
+ end
+
def test_help_when_a_generator_with_required_arguments_is_invoked_without_arguments
output = capture(:stdout){ Rails::Generators.invoke :model, [] }
assert_match(/Description:/, output)
diff --git a/railties/test/path_generation_test.rb b/railties/test/path_generation_test.rb
index e3dfcdfb7d..d22374a1ff 100644
--- a/railties/test/path_generation_test.rb
+++ b/railties/test/path_generation_test.rb
@@ -4,18 +4,16 @@ require 'active_support/core_ext/object/json'
require 'rails'
require 'rails/application'
-ROUTING = ActionDispatch::Routing
-
class PathGenerationTest < ActiveSupport::TestCase
attr_reader :app
- class TestSet < ROUTING::RouteSet
+ class TestSet < ActionDispatch::Routing::RouteSet
def initialize(block)
@block = block
super()
end
- class Dispatcher < ROUTING::RouteSet::Dispatcher
+ class Dispatcher < ActionDispatch::Routing::RouteSet::Dispatcher
def initialize(defaults, set, block)
super(defaults)
@block = block