aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml3
-rw-r--r--Gemfile4
-rw-r--r--actionmailer/CHANGELOG.md14
-rw-r--r--actionmailer/lib/action_mailer/base.rb3
-rw-r--r--actionmailer/lib/action_mailer/mail_helper.rb2
-rw-r--r--actionmailer/test/base_test.rb14
-rw-r--r--actionmailer/test/fixtures/base_test/late_inline_attachment_mailer/on_render.erb7
-rw-r--r--actionpack/lib/action_dispatch/http/request.rb4
-rw-r--r--actionpack/test/dispatch/request_test.rb6
-rw-r--r--actionview/CHANGELOG.md8
-rw-r--r--actionview/lib/action_view/base.rb4
-rw-r--r--actionview/lib/action_view/buffers.rb7
-rw-r--r--actionview/lib/action_view/helpers/asset_url_helper.rb2
-rw-r--r--actionview/lib/action_view/helpers/form_helper.rb18
-rw-r--r--actionview/lib/action_view/helpers/url_helper.rb36
-rw-r--r--actionview/lib/action_view/template/handlers/erb.rb4
-rw-r--r--actionview/test/fixtures/test/nil_return.erb1
-rw-r--r--actionview/test/template/asset_tag_helper_test.rb11
-rw-r--r--actionview/test/template/compiled_templates_test.rb4
-rw-r--r--activejob/README.md19
-rw-r--r--activejob/Rakefile90
-rw-r--r--activejob/activejob.gemspec2
-rw-r--r--activejob/lib/active_job/arguments.rb22
-rw-r--r--activejob/lib/active_job/callbacks.rb12
-rw-r--r--activejob/lib/active_job/core.rb2
-rw-r--r--activejob/lib/active_job/enqueuing.rb8
-rw-r--r--activejob/lib/active_job/execution.rb2
-rw-r--r--activejob/lib/active_job/logging.rb2
-rw-r--r--activejob/lib/active_job/queue_adapters.rb34
-rw-r--r--activejob/lib/active_job/queue_adapters/backburner_adapter.rb16
-rw-r--r--activejob/lib/active_job/queue_adapters/delayed_job_adapter.rb16
-rw-r--r--activejob/lib/active_job/queue_adapters/inline_adapter.rb12
-rw-r--r--activejob/lib/active_job/queue_adapters/qu_adapter.rb19
-rw-r--r--activejob/lib/active_job/queue_adapters/que_adapter.rb18
-rw-r--r--activejob/lib/active_job/queue_adapters/queue_classic_adapter.rb20
-rw-r--r--activejob/lib/active_job/queue_adapters/resque_adapter.rb17
-rw-r--r--activejob/lib/active_job/queue_adapters/sidekiq_adapter.rb18
-rw-r--r--activejob/lib/active_job/queue_adapters/sneakers_adapter.rb18
-rw-r--r--activejob/lib/active_job/queue_adapters/sucker_punch_adapter.rb21
-rw-r--r--activejob/lib/active_job/queue_adapters/test_adapter.rb60
-rw-r--r--activejob/test/cases/argument_serialization_test.rb76
-rw-r--r--activejob/test/cases/parameters_test.rb77
-rw-r--r--activejob/test/helper.rb2
-rw-r--r--activemodel/CHANGELOG.md19
-rw-r--r--activemodel/lib/active_model/secure_password.rb2
-rw-r--r--activerecord/CHANGELOG.md83
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb2
-rw-r--r--activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb2
-rw-r--r--activerecord/lib/active_record/autosave_association.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/transaction.rb16
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb7
-rw-r--r--activerecord/lib/active_record/core.rb7
-rw-r--r--activerecord/lib/active_record/counter_cache.rb2
-rw-r--r--activerecord/lib/active_record/fixtures.rb3
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb14
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb16
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder.rb4
-rw-r--r--activerecord/lib/active_record/schema_dumper.rb2
-rw-r--r--activerecord/lib/active_record/type.rb1
-rw-r--r--activerecord/lib/active_record/type/decorator.rb14
-rw-r--r--activerecord/lib/active_record/type/serialized.rb6
-rw-r--r--activerecord/test/cases/adapters/postgresql/schema_test.rb26
-rw-r--r--activerecord/test/cases/attribute_methods_test.rb16
-rw-r--r--activerecord/test/cases/autosave_association_test.rb24
-rw-r--r--activerecord/test/cases/finder_test.rb33
-rw-r--r--activerecord/test/cases/schema_dumper_test.rb5
-rw-r--r--activerecord/test/cases/transactions_test.rb31
-rw-r--r--activesupport/CHANGELOG.md79
-rw-r--r--activesupport/lib/active_support/concern.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/module/delegation.rb10
-rw-r--r--activesupport/lib/active_support/core_ext/string/filters.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/string/inflections.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/time/calculations.rb19
-rw-r--r--activesupport/lib/active_support/duration.rb14
-rw-r--r--activesupport/lib/active_support/message_verifier.rb1
-rw-r--r--activesupport/lib/active_support/multibyte/unicode.rb1
-rw-r--r--activesupport/test/core_ext/duration_test.rb17
-rw-r--r--activesupport/test/core_ext/module_test.rb11
-rw-r--r--activesupport/test/core_ext/object/duplicable_test.rb2
-rw-r--r--activesupport/test/core_ext/string_ext_test.rb6
-rw-r--r--activesupport/test/core_ext/time_ext_test.rb7
-rw-r--r--activesupport/test/message_verifier_test.rb7
-rw-r--r--guides/source/3_0_release_notes.md2
-rw-r--r--guides/source/4_2_release_notes.md73
-rw-r--r--guides/source/active_job_basics.md44
-rw-r--r--guides/source/active_record_querying.md6
-rw-r--r--guides/source/active_support_core_extensions.md2
-rw-r--r--guides/source/command_line.md2
-rw-r--r--guides/source/configuring.md2
-rw-r--r--guides/source/debugging_rails_applications.md2
-rw-r--r--guides/source/maintenance_policy.md9
-rw-r--r--guides/source/upgrading_ruby_on_rails.md44
-rw-r--r--railties/CHANGELOG.md19
-rw-r--r--railties/lib/rails/application.rb2
-rw-r--r--railties/lib/rails/application/default_middleware_stack.rb6
-rw-r--r--railties/lib/rails/commands/server.rb6
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt7
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb5
-rw-r--r--railties/lib/rails/generators/test_unit/job/job_generator.rb2
-rw-r--r--railties/lib/rails/test_unit/testing.rake6
-rw-r--r--railties/test/application/configuration_test.rb2
-rw-r--r--railties/test/application/middleware_test.rb11
-rw-r--r--railties/test/application/test_runner_test.rb17
-rw-r--r--railties/test/generators/plugin_generator_test.rb14
104 files changed, 1016 insertions, 520 deletions
diff --git a/.travis.yml b/.travis.yml
index 985956363a..c213700687 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -28,9 +28,10 @@ matrix:
env: "GEM=ar:mysql"
- rvm: 2.0.0
env: "GEM=ar:mysql"
+ - rvm: ruby-head
+ env: "GEM=ar:mysql"
- rvm: rbx-2
- rvm: jruby
- - rvm: ruby-head
fast_finish: true
notifications:
email: false
diff --git a/Gemfile b/Gemfile
index eb7bdc2bdb..08acdc3767 100644
--- a/Gemfile
+++ b/Gemfile
@@ -2,13 +2,15 @@ source 'https://rubygems.org'
gemspec
+# We need a newish Rake since Active Job sets its test tasks' descriptions.
+gem 'rake', '>= 10.3'
+
# This needs to be with require false as it is
# loaded after loading the test library to
# ensure correct loading order
gem 'mocha', '~> 0.14', require: false
gem 'rack-cache', '~> 1.2'
-# TODO: Release this gem
gem 'jquery-rails', '~> 4.0.0.beta2'
gem 'coffee-rails', '~> 4.0.0'
# TODO: Release this gem
diff --git a/actionmailer/CHANGELOG.md b/actionmailer/CHANGELOG.md
index 1ff4db41a3..e2900c2d10 100644
--- a/actionmailer/CHANGELOG.md
+++ b/actionmailer/CHANGELOG.md
@@ -1,13 +1,19 @@
-* Added #deliver_later, #deliver_now and deprecate #deliver in favour of
- #deliver_now. #deliver_later will enqueue a job to render and deliver
+* Attachments can be added while rendering the mail template.
+
+ Fixes #16974.
+
+ *Christian Felder*
+
+* Added `#deliver_later`, `#deliver_now` and deprecate `#deliver` in favour of
+ `#deliver_now`. `#deliver_later` will enqueue a job to render and deliver
the mail instead of delivering it right at that moment. The job is enqueued
using the new Active Job framework in Rails, and will use whatever queue is
configured for Rails.
*DHH/Abdelkader Boudih/Cristian Bica*
-* Make ActionMailer::Previews methods class methods. Previously they were
- instance methods and ActionMailer tries to render a message when they
+* Make `ActionMailer::Previews` methods class methods. Previously they were
+ instance methods and `ActionMailer` tries to render a message when they
are called.
*Cristian Bica*
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb
index f539fc53c6..1ab68d2953 100644
--- a/actionmailer/lib/action_mailer/base.rb
+++ b/actionmailer/lib/action_mailer/base.rb
@@ -775,7 +775,6 @@ module ActionMailer
def mail(headers = {}, &block)
return @_message if @_mail_was_called && headers.blank? && !block
- @_mail_was_called = true
m = @_message
# At the beginning, do not consider class default for content_type
@@ -803,6 +802,8 @@ module ActionMailer
# Render the templates and blocks
responses = collect_responses(headers, &block)
+ @_mail_was_called = true
+
create_parts_from_responses(m, responses)
# Setup content type, reapply charset and handle parts order
diff --git a/actionmailer/lib/action_mailer/mail_helper.rb b/actionmailer/lib/action_mailer/mail_helper.rb
index 483277af04..cc7935a7e0 100644
--- a/actionmailer/lib/action_mailer/mail_helper.rb
+++ b/actionmailer/lib/action_mailer/mail_helper.rb
@@ -29,7 +29,7 @@ module ActionMailer
# Access the message attachments list.
def attachments
- @_message.attachments
+ mailer.attachments
end
# Returns +text+ wrapped at +len+ columns and indented +indent+ spaces.
diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb
index d00f5aea47..dcb6959543 100644
--- a/actionmailer/test/base_test.rb
+++ b/actionmailer/test/base_test.rb
@@ -259,6 +259,20 @@ class BaseTest < ActiveSupport::TestCase
assert_match(/Can't add attachments after `mail` was called./, e.message)
end
+ test "adding inline attachments while rendering mail works" do
+ class LateInlineAttachmentMailer < ActionMailer::Base
+ def on_render
+ mail from: "welcome@example.com", to: "to@example.com"
+ end
+ end
+
+ mail = LateInlineAttachmentMailer.on_render
+ assert_nothing_raised { mail.message }
+
+ assert_equal ["image/jpeg; filename=controller_attachments.jpg",
+ "image/jpeg; filename=attachments.jpg"], mail.attachments.inline.map {|a| a['Content-Type'].to_s }
+ end
+
test "accessing attachments works after mail was called" do
class LateAttachmentAccessorMailer < ActionMailer::Base
def welcome
diff --git a/actionmailer/test/fixtures/base_test/late_inline_attachment_mailer/on_render.erb b/actionmailer/test/fixtures/base_test/late_inline_attachment_mailer/on_render.erb
new file mode 100644
index 0000000000..6decd3bb31
--- /dev/null
+++ b/actionmailer/test/fixtures/base_test/late_inline_attachment_mailer/on_render.erb
@@ -0,0 +1,7 @@
+<h1>Adding an inline image while rendering</h1>
+
+<% controller.attachments.inline["controller_attachments.jpg"] = 'via controller.attachments.inline' %>
+<%= image_tag attachments['controller_attachments.jpg'].url %>
+
+<% attachments.inline["attachments.jpg"] = 'via attachments.inline' %>
+<%= image_tag attachments['attachments.jpg'].url %>
diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb
index c441f7f95b..e854dd266c 100644
--- a/actionpack/lib/action_dispatch/http/request.rb
+++ b/actionpack/lib/action_dispatch/http/request.rb
@@ -298,7 +298,7 @@ module ActionDispatch
# Override Rack's GET method to support indifferent access
def GET
@env["action_dispatch.request.query_parameters"] ||= Utils.deep_munge(normalize_encode_params(super || {}))
- rescue TypeError, Rack::Utils::InvalidParameterError => e
+ rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e
raise ActionController::BadRequest.new(:query, e)
end
alias :query_parameters :GET
@@ -306,7 +306,7 @@ module ActionDispatch
# Override Rack's POST method to support indifferent access
def POST
@env["action_dispatch.request.request_parameters"] ||= Utils.deep_munge(normalize_encode_params(super || {}))
- rescue TypeError, Rack::Utils::InvalidParameterError => e
+ rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e
raise ActionController::BadRequest.new(:request, e)
end
alias :request_parameters :POST
diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb
index 00b80c7357..a58306ea0a 100644
--- a/actionpack/test/dispatch/request_test.rb
+++ b/actionpack/test/dispatch/request_test.rb
@@ -915,7 +915,7 @@ class RequestParameters < BaseRequestTest
2.times do
assert_raises(ActionController::BadRequest) do
- # rack will raise a TypeError when parsing this query string
+ # rack will raise a Rack::Utils::ParameterTypeError when parsing this query string
request.parameters
end
end
@@ -941,7 +941,7 @@ class RequestParameters < BaseRequestTest
)
assert_raises(ActionController::BadRequest) do
- # rack will raise a TypeError when parsing this query string
+ # rack will raise a Rack::Utils::ParameterTypeError when parsing this query string
request.parameters
end
end
@@ -950,7 +950,7 @@ class RequestParameters < BaseRequestTest
request = stub_request("QUERY_STRING" => "x[y]=1&x[y][][w]=2")
e = assert_raises(ActionController::BadRequest) do
- # rack will raise a TypeError when parsing this query string
+ # rack will raise a Rack::Utils::ParameterTypeError when parsing this query string
request.parameters
end
diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md
index 5e0b134ba4..ed58933472 100644
--- a/actionview/CHANGELOG.md
+++ b/actionview/CHANGELOG.md
@@ -127,12 +127,12 @@
Before:
- #=> favicon_link_tag 'myicon.ico'
+ # => favicon_link_tag 'myicon.ico'
<link href="/assets/myicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon" />
After:
- #=> favicon_link_tag 'myicon.ico'
+ # => favicon_link_tag 'myicon.ico'
<link href="/assets/myicon.ico" rel="shortcut icon" type="image/x-icon" />
*Geoffroy Lorieux*
@@ -145,7 +145,7 @@
*Joost Baaij*
-* `collection_check_boxes` respects `:index` option for the hidden filed name.
+* `collection_check_boxes` respects `:index` option for the hidden field name.
Fixes #14147.
@@ -166,7 +166,7 @@
*Vasiliy Ermolovich*
-* Fixed a problem where the default options for the `button_tag` helper is not
+* Fixed a problem where the default options for the `button_tag` helper are not
applied correctly.
Fixes #14254.
diff --git a/actionview/lib/action_view/base.rb b/actionview/lib/action_view/base.rb
index d1bade0d3d..3071a13655 100644
--- a/actionview/lib/action_view/base.rb
+++ b/actionview/lib/action_view/base.rb
@@ -33,7 +33,9 @@ module ActionView #:nodoc:
#
# If you absolutely must write from within a function use +concat+.
#
- # <%- and -%> suppress leading and trailing whitespace, including the trailing newline, and can be used interchangeably with <% and %>.
+ # When on a line that only contains whitespaces except for the tag, <% %> suppress leading and trailing whitespace,
+ # including the trailing newline. <% %> and <%- -%> are the same.
+ # Note however that <%= %> and <%= -%> are different: only the latter removes trailing whitespaces.
#
# === Using sub templates
#
diff --git a/actionview/lib/action_view/buffers.rb b/actionview/lib/action_view/buffers.rb
index 361a0dccbe..be5d86b1dc 100644
--- a/actionview/lib/action_view/buffers.rb
+++ b/actionview/lib/action_view/buffers.rb
@@ -13,10 +13,11 @@ module ActionView
end
alias :append= :<<
- def safe_concat(value)
- return self if value.nil?
- super(value.to_s)
+ def safe_expr_append=(val)
+ return self if val.nil?
+ safe_concat val.to_s
end
+
alias :safe_append= :safe_concat
end
diff --git a/actionview/lib/action_view/helpers/asset_url_helper.rb b/actionview/lib/action_view/helpers/asset_url_helper.rb
index 9e8d005ec7..29733442c1 100644
--- a/actionview/lib/action_view/helpers/asset_url_helper.rb
+++ b/actionview/lib/action_view/helpers/asset_url_helper.rb
@@ -121,8 +121,8 @@ module ActionView
# asset_path "application", type: :stylesheet # => /assets/application.css
# asset_path "http://www.example.com/js/xmlhr.js" # => http://www.example.com/js/xmlhr.js
def asset_path(source, options = {})
- return "" unless source.present?
source = source.to_s
+ return "" unless source.present?
return source if source =~ URI_REGEXP
tail, source = source[/([\?#].+)$/], source.sub(/([\?#].+)$/, '')
diff --git a/actionview/lib/action_view/helpers/form_helper.rb b/actionview/lib/action_view/helpers/form_helper.rb
index 09843ca70d..038f9e384b 100644
--- a/actionview/lib/action_view/helpers/form_helper.rb
+++ b/actionview/lib/action_view/helpers/form_helper.rb
@@ -51,9 +51,7 @@ module ActionView
# The HTML generated for this would be (modulus formatting):
#
# <form action="/people" class="new_person" id="new_person" method="post">
- # <div style="display:none">
- # <input name="authenticity_token" type="hidden" value="NrOp5bsjoLRuK8IW5+dQEYjKGUJDe7TQoZVvq95Wteg=" />
- # </div>
+ # <input name="authenticity_token" type="hidden" value="NrOp5bsjoLRuK8IW5+dQEYjKGUJDe7TQoZVvq95Wteg=" />
# <label for="person_first_name">First name</label>:
# <input id="person_first_name" name="person[first_name]" type="text" /><br />
#
@@ -81,10 +79,8 @@ module ActionView
# the code above as is would yield instead:
#
# <form action="/people/256" class="edit_person" id="edit_person_256" method="post">
- # <div style="display:none">
- # <input name="_method" type="hidden" value="patch" />
- # <input name="authenticity_token" type="hidden" value="NrOp5bsjoLRuK8IW5+dQEYjKGUJDe7TQoZVvq95Wteg=" />
- # </div>
+ # <input name="_method" type="hidden" value="patch" />
+ # <input name="authenticity_token" type="hidden" value="NrOp5bsjoLRuK8IW5+dQEYjKGUJDe7TQoZVvq95Wteg=" />
# <label for="person_first_name">First name</label>:
# <input id="person_first_name" name="person[first_name]" type="text" value="John" /><br />
#
@@ -315,9 +311,7 @@ module ActionView
# The HTML generated for this would be:
#
# <form action='http://www.example.com' method='post' data-remote='true'>
- # <div style='display:none'>
- # <input name='_method' type='hidden' value='patch' />
- # </div>
+ # <input name='_method' type='hidden' value='patch' />
# ...
# </form>
#
@@ -333,9 +327,7 @@ module ActionView
# The HTML generated for this would be:
#
# <form action='http://www.example.com' method='post' data-behavior='autosave' name='go'>
- # <div style='display:none'>
- # <input name='_method' type='hidden' value='patch' />
- # </div>
+ # <input name='_method' type='hidden' value='patch' />
# ...
# </form>
#
diff --git a/actionview/lib/action_view/helpers/url_helper.rb b/actionview/lib/action_view/helpers/url_helper.rb
index c3be47133c..364414da05 100644
--- a/actionview/lib/action_view/helpers/url_helper.rb
+++ b/actionview/lib/action_view/helpers/url_helper.rb
@@ -229,59 +229,51 @@ module ActionView
# ==== Examples
# <%= button_to "New", action: "new" %>
# # => "<form method="post" action="/controller/new" class="button_to">
- # # <div><input value="New" type="submit" /></div>
+ # # <input value="New" type="submit" />
# # </form>"
#
# <%= button_to "New", new_articles_path %>
# # => "<form method="post" action="/articles/new" class="button_to">
- # # <div><input value="New" type="submit" /></div>
+ # # <input value="New" type="submit" />
# # </form>"
#
# <%= button_to [:make_happy, @user] do %>
# Make happy <strong><%= @user.name %></strong>
# <% end %>
# # => "<form method="post" action="/users/1/make_happy" class="button_to">
- # # <div>
- # # <button type="submit">
- # # Make happy <strong><%= @user.name %></strong>
- # # </button>
- # # </div>
+ # # <button type="submit">
+ # # Make happy <strong><%= @user.name %></strong>
+ # # </button>
# # </form>"
#
# <%= button_to "New", { action: "new" }, form_class: "new-thing" %>
# # => "<form method="post" action="/controller/new" class="new-thing">
- # # <div><input value="New" type="submit" /></div>
+ # # <input value="New" type="submit" />
# # </form>"
#
#
# <%= button_to "Create", { action: "create" }, remote: true, form: { "data-type" => "json" } %>
# # => "<form method="post" action="/images/create" class="button_to" data-remote="true" data-type="json">
- # # <div>
- # # <input value="Create" type="submit" />
- # # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6"/>
- # # </div>
+ # # <input value="Create" type="submit" />
+ # # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6"/>
# # </form>"
#
#
# <%= button_to "Delete Image", { action: "delete", id: @image.id },
# method: :delete, data: { confirm: "Are you sure?" } %>
# # => "<form method="post" action="/images/delete/1" class="button_to">
- # # <div>
- # # <input type="hidden" name="_method" value="delete" />
- # # <input data-confirm='Are you sure?' value="Delete Image" type="submit" />
- # # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6"/>
- # # </div>
+ # # <input type="hidden" name="_method" value="delete" />
+ # # <input data-confirm='Are you sure?' value="Delete Image" type="submit" />
+ # # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6"/>
# # </form>"
#
#
# <%= button_to('Destroy', 'http://www.example.com',
# method: "delete", remote: true, data: { confirm: 'Are you sure?', disable_with: 'loading...' }) %>
# # => "<form class='button_to' method='post' action='http://www.example.com' data-remote='true'>
- # # <div>
- # # <input name='_method' value='delete' type='hidden' />
- # # <input value='Destroy' type='submit' data-disable-with='loading...' data-confirm='Are you sure?' />
- # # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6"/>
- # # </div>
+ # # <input name='_method' value='delete' type='hidden' />
+ # # <input value='Destroy' type='submit' data-disable-with='loading...' data-confirm='Are you sure?' />
+ # # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6"/>
# # </form>"
# #
def button_to(name = nil, options = nil, html_options = nil, &block)
diff --git a/actionview/lib/action_view/template/handlers/erb.rb b/actionview/lib/action_view/template/handlers/erb.rb
index 4523060442..3c2224fbf5 100644
--- a/actionview/lib/action_view/template/handlers/erb.rb
+++ b/actionview/lib/action_view/template/handlers/erb.rb
@@ -49,9 +49,9 @@ module ActionView
def add_expr_escaped(src, code)
flush_newline_if_pending(src)
if code =~ BLOCK_EXPR
- src << "@output_buffer.safe_append= " << code
+ src << "@output_buffer.safe_expr_append= " << code
else
- src << "@output_buffer.safe_append=(" << code << ");"
+ src << "@output_buffer.safe_expr_append=(" << code << ");"
end
end
diff --git a/actionview/test/fixtures/test/nil_return.erb b/actionview/test/fixtures/test/nil_return.erb
new file mode 100644
index 0000000000..90ce3881f6
--- /dev/null
+++ b/actionview/test/fixtures/test/nil_return.erb
@@ -0,0 +1 @@
+This is nil: <%== nil %>
diff --git a/actionview/test/template/asset_tag_helper_test.rb b/actionview/test/template/asset_tag_helper_test.rb
index d789a5ca27..df8547ef85 100644
--- a/actionview/test/template/asset_tag_helper_test.rb
+++ b/actionview/test/template/asset_tag_helper_test.rb
@@ -535,6 +535,17 @@ class AssetTagHelperTest < ActionView::TestCase
assert_equal copy, source
end
+ class PlaceholderImage
+ def blank?; true; end
+ def to_s; 'no-image-yet.png'; end
+ end
+ def test_image_tag_with_blank_placeholder
+ assert_equal '<img alt="" src="/images/no-image-yet.png" />', image_tag(PlaceholderImage.new, alt: "")
+ end
+ def test_image_path_with_blank_placeholder
+ assert_equal '/images/no-image-yet.png', image_path(PlaceholderImage.new)
+ end
+
def test_image_path_with_asset_host_proc_returning_nil
@controller.config.asset_host = Proc.new do |source|
unless source.end_with?("tiff")
diff --git a/actionview/test/template/compiled_templates_test.rb b/actionview/test/template/compiled_templates_test.rb
index b84aca6746..f6c1283b92 100644
--- a/actionview/test/template/compiled_templates_test.rb
+++ b/actionview/test/template/compiled_templates_test.rb
@@ -5,6 +5,10 @@ class CompiledTemplatesTest < ActiveSupport::TestCase
ActionView::LookupContext::DetailsKey.clear
end
+ def test_template_with_nil_erb_return
+ assert_equal "This is nil: \n", render(:template => "test/nil_return")
+ end
+
def test_template_gets_recompiled_when_using_different_keys_in_local_assigns
assert_equal "one", render(:file => "test/render_file_with_locals_and_default")
assert_equal "two", render(:file => "test/render_file_with_locals_and_default", :locals => { :secret => "two" })
diff --git a/activejob/README.md b/activejob/README.md
index 5734ca413e..b5d27272b1 100644
--- a/activejob/README.md
+++ b/activejob/README.md
@@ -24,9 +24,9 @@ Set the queue adapter for Active Job:
``` ruby
ActiveJob::Base.queue_adapter = :inline # default queue adapter
-# Adapters currently supported: :backburner, :delayed_job, :qu, :que, :queue_classic,
-# :resque, :sidekiq, :sneakers, :sucker_punch
```
+Note: To learn how to use your preferred queueing backend see its adapter
+documentation at ActiveJob::QueueAdapters.
Declare a job like so:
@@ -88,18 +88,9 @@ by default has been mixed into Active Record classes.
## Supported queueing systems
-We currently have adapters for:
-
-* [Backburner](https://github.com/nesquena/backburner)
-* [Delayed Job](https://github.com/collectiveidea/delayed_job)
-* [Qu](https://github.com/bkeepers/qu)
-* [Que](https://github.com/chanks/que)
-* [QueueClassic](https://github.com/ryandotsmith/queue_classic)
-* [Resque 1.x](https://github.com/resque/resque)
-* [Sidekiq](https://github.com/mperham/sidekiq)
-* [Sneakers](https://github.com/jondot/sneakers)
-* [Sucker Punch](https://github.com/brandonhilkert/sucker_punch)
-
+Active Job has built-in adapters for multiple queueing backends (Sidekiq,
+Resque, Delayed Job and others). To get an up-to-date list of the adapters
+see the API Documentation for [ActiveJob::QueueAdapters](http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters.html).
## Auxiliary gems
diff --git a/activejob/Rakefile b/activejob/Rakefile
index e918428459..dadd0baf82 100644
--- a/activejob/Rakefile
+++ b/activejob/Rakefile
@@ -1,59 +1,45 @@
require 'rake/testtask'
require 'rubygems/package_task'
-dir = File.dirname(__FILE__)
-
-def run_without_aborting(*tasks)
- errors = []
-
- tasks.each do |task|
- begin
- Rake::Task[task].invoke
- rescue Exception
- errors << task
- end
- end
-
- abort "Errors running #{errors.join(', ')}" if errors.any?
-end
-
-task default: :test
-
ACTIVEJOB_ADAPTERS = %w(inline delayed_job qu que queue_classic resque sidekiq sneakers sucker_punch backburner)
ACTIVEJOB_ADAPTERS -= %w(queue_classic) if defined?(JRUBY_VERSION)
-desc 'Run all adapter tests'
-task :test do
- tasks = ACTIVEJOB_ADAPTERS.map{|a| "test_#{a}" }
- run_without_aborting(*tasks)
-end
+task default: :test
+task test: 'test:default'
namespace :test do
+ desc 'Run all adapter tests'
+ task :default do
+ run_without_aborting ACTIVEJOB_ADAPTERS.map { |a| "test:#{a}" }
+ end
+
desc 'Run all adapter tests in isolation'
task :isolated do
- tasks = ACTIVEJOB_ADAPTERS.map{|a| "isolated_test_#{a}" }
- run_without_aborting(*tasks)
+ run_without_aborting ACTIVEJOB_ADAPTERS.map { |a| "test:isolated:#{a}" }
end
- desc 'Run all adapter integration tests'
+ desc 'Run integration tests for all adapters'
task :integration do
- tasks = ACTIVEJOB_ADAPTERS.map{|a| "integration_test_#{a}" }
- run_without_aborting(*tasks)
+ run_without_aborting ACTIVEJOB_ADAPTERS.map { |a| "test:integration:#{a}" }
+ end
+
+ task 'env:integration' do
+ ENV['AJ_INTEGRATION_TESTS'] = "1"
end
-end
+ ACTIVEJOB_ADAPTERS.each do |adapter|
+ task("env:#{adapter}") { ENV['AJADAPTER'] = adapter }
-ACTIVEJOB_ADAPTERS.each do |adapter|
- namespace :test do
- Rake::TestTask.new(adapter => "#{adapter}:env") do |t|
- t.description = ""
+ Rake::TestTask.new(adapter => "test:env:#{adapter}") do |t|
+ t.description = "Run adapter tests for #{adapter}"
t.libs << 'test'
t.test_files = FileList['test/cases/**/*_test.rb']
t.verbose = true
end
namespace :isolated do
- task adapter => "#{adapter}:env" do
+ task adapter => "test:env:#{adapter}" do
+ dir = File.dirname(__FILE__)
Dir.glob("#{dir}/test/cases/**/*_test.rb").all? do |file|
sh(Gem.ruby, '-w', "-I#{dir}/lib", "-I#{dir}/test", file)
end or raise 'Failures'
@@ -61,42 +47,28 @@ ACTIVEJOB_ADAPTERS.each do |adapter|
end
namespace :integration do
- Rake::TestTask.new(adapter => "#{adapter}:env") do |t|
- t.description = ""
+ Rake::TestTask.new(adapter => ["test:env:#{adapter}", 'test:env:integration']) do |t|
+ t.description = "Run integration tests for #{adapter}"
t.libs << 'test'
t.test_files = FileList['test/integration/**/*_test.rb']
t.verbose = true
end
end
end
+end
- namespace adapter do
- task test: "test_#{adapter}"
- task isolated_test: "isolated_test_#{adapter}"
-
- task(:env) { ENV['AJADAPTER'] = adapter }
-
- namespace :isolated do
- task(:env) { ENV['AJADAPTER'] = adapter }
- end
+def run_without_aborting(tasks)
+ errors = []
- namespace :integration do
- task(:env) do
- ENV['AJADAPTER'] = adapter
- ENV['AJ_INTEGRATION_TESTS'] = "1"
- end
+ tasks.each do |task|
+ begin
+ Rake::Task[task].invoke
+ rescue Exception
+ errors << task
end
end
-
- desc "Run #{adapter} tests"
- task "test_#{adapter}" => ["#{adapter}:env", "test:#{adapter}"]
-
- desc "Run #{adapter} tests in isolation"
- task "isolated_test_#{adapter}" => ["#{adapter}:isolated:env", "test:isolated:#{adapter}"]
-
- desc "Run #{adapter} integration tests"
- task "integration_test_#{adapter}" => ["#{adapter}:integration:env", "test:integration:#{adapter}"]
+ abort "Errors running #{errors.join(', ')}" if errors.any?
end
diff --git a/activejob/activejob.gemspec b/activejob/activejob.gemspec
index 9944b2a1bd..a9be2a8f00 100644
--- a/activejob/activejob.gemspec
+++ b/activejob/activejob.gemspec
@@ -19,5 +19,5 @@ Gem::Specification.new do |s|
s.require_path = 'lib'
s.add_dependency 'activesupport', version
- s.add_dependency 'globalid', '>= 0.2.3'
+ s.add_dependency 'globalid', '>= 0.3.0'
end
diff --git a/activejob/lib/active_job/arguments.rb b/activejob/lib/active_job/arguments.rb
index 69a4ad928d..099ea4d169 100644
--- a/activejob/lib/active_job/arguments.rb
+++ b/activejob/lib/active_job/arguments.rb
@@ -37,14 +37,16 @@ module ActiveJob
private
def serialize_argument(argument)
case argument
- when GlobalID::Identification
- argument.global_id.to_s
when *TYPE_WHITELIST
argument
+ when GlobalID::Identification
+ argument.to_global_id.to_s
when Array
argument.map { |arg| serialize_argument(arg) }
when Hash
- Hash[ argument.map { |key, value| [ serialize_hash_key(key), serialize_argument(value) ] } ]
+ argument.each_with_object({}) do |(key, value), hash|
+ hash[serialize_hash_key(key)] = serialize_argument(value)
+ end
else
raise SerializationError.new("Unsupported argument type: #{argument.class.name}")
end
@@ -52,14 +54,18 @@ module ActiveJob
def deserialize_argument(argument)
case argument
+ when String
+ GlobalID::Locator.locate(argument) || argument
+ when *TYPE_WHITELIST
+ argument
when Array
argument.map { |arg| deserialize_argument(arg) }
when Hash
- Hash[ argument.map { |key, value| [ key, deserialize_argument(value) ] } ].with_indifferent_access
- when String, GlobalID
- GlobalID::Locator.locate(argument) || argument
+ argument.each_with_object({}.with_indifferent_access) do |(key, value), hash|
+ hash[key] = deserialize_argument(value)
+ end
else
- argument
+ raise ArgumentError, "Can only deserialize primitive arguments: #{argument.inspect}"
end
end
@@ -68,7 +74,7 @@ module ActiveJob
when String, Symbol
key.to_s
else
- raise SerializationError.new("Unsupported hash key type: #{key.class.name}")
+ raise SerializationError.new("Only string and symbol hash keys may be serialized as job arguments, but #{key.inspect} is a #{key.class}")
end
end
end
diff --git a/activejob/lib/active_job/callbacks.rb b/activejob/lib/active_job/callbacks.rb
index cafa3438c0..29e2a878b4 100644
--- a/activejob/lib/active_job/callbacks.rb
+++ b/activejob/lib/active_job/callbacks.rb
@@ -36,7 +36,7 @@ module ActiveJob
# def perform(video_id)
# Video.find(video_id).process
# end
- # end
+ # end
#
def before_perform(*filters, &blk)
set_callback(:perform, :before, *filters, &blk)
@@ -55,7 +55,7 @@ module ActiveJob
# def perform(video_id)
# Video.find(video_id).process
# end
- # end
+ # end
#
def after_perform(*filters, &blk)
set_callback(:perform, :after, *filters, &blk)
@@ -75,7 +75,7 @@ module ActiveJob
# def perform(video_id)
# Video.find(video_id).process
# end
- # end
+ # end
#
def around_perform(*filters, &blk)
set_callback(:perform, :around, *filters, &blk)
@@ -94,7 +94,7 @@ module ActiveJob
# def perform(video_id)
# Video.find(video_id).process
# end
- # end
+ # end
#
def before_enqueue(*filters, &blk)
set_callback(:enqueue, :before, *filters, &blk)
@@ -113,7 +113,7 @@ module ActiveJob
# def perform(video_id)
# Video.find(video_id).process
# end
- # end
+ # end
#
def after_enqueue(*filters, &blk)
set_callback(:enqueue, :after, *filters, &blk)
@@ -134,7 +134,7 @@ module ActiveJob
# def perform(video_id)
# Video.find(video_id).process
# end
- # end
+ # end
#
def around_enqueue(*filters, &blk)
set_callback(:enqueue, :around, *filters, &blk)
diff --git a/activejob/lib/active_job/core.rb b/activejob/lib/active_job/core.rb
index b6dd03a0bc..9db23cc9da 100644
--- a/activejob/lib/active_job/core.rb
+++ b/activejob/lib/active_job/core.rb
@@ -13,7 +13,7 @@ module ActiveJob
# Job Identifier
attr_accessor :job_id
- # Queue on which the job should be run on.
+ # Queue in which the job will reside.
attr_writer :queue_name
end
diff --git a/activejob/lib/active_job/enqueuing.rb b/activejob/lib/active_job/enqueuing.rb
index e8bc44cbc4..74bcc1fa5d 100644
--- a/activejob/lib/active_job/enqueuing.rb
+++ b/activejob/lib/active_job/enqueuing.rb
@@ -5,9 +5,9 @@ module ActiveJob
extend ActiveSupport::Concern
module ClassMethods
- # Push a job onto the queue. The arguments must be legal JSON types
+ # Push a job onto the queue. The arguments must be legal JSON types
# (string, int, float, nil, true, false, hash or array) or
- # GlobalID::Identification instances. Arbitrary Ruby objects
+ # GlobalID::Identification instances. Arbitrary Ruby objects
# are not supported.
#
# Returns an instance of the job class queued with args available in
@@ -22,7 +22,7 @@ module ActiveJob
end
end
- # Reschedule the job to be re-executed. This is usefull in combination
+ # Reschedule the job to be re-executed. This is useful in combination
# with the +rescue_from+ option. When you rescue an exception from your job
# you can ask Active Job to retry performing your job.
#
@@ -45,7 +45,7 @@ module ActiveJob
enqueue options
end
- # Equeue the job to be performed by the queue adapter.
+ # Enqueues the job to be performed by the queue adapter.
#
# ==== Options
# * <tt>:wait</tt> - Enqueues the job with the specified delay
diff --git a/activejob/lib/active_job/execution.rb b/activejob/lib/active_job/execution.rb
index d6d67c46e3..7ff857206d 100644
--- a/activejob/lib/active_job/execution.rb
+++ b/activejob/lib/active_job/execution.rb
@@ -22,7 +22,7 @@ module ActiveJob
end
# Performs the job immediately. The job is not sent to the queueing adapter
- # and will block the execution until it's finished.
+ # but directly executed by blocking the execution of others until it's finished.
#
# MyJob.new(*args).perform_now
def perform_now
diff --git a/activejob/lib/active_job/logging.rb b/activejob/lib/active_job/logging.rb
index 962005cd15..bb96668cfb 100644
--- a/activejob/lib/active_job/logging.rb
+++ b/activejob/lib/active_job/logging.rb
@@ -50,7 +50,7 @@ module ActiveJob
logger.formatter.current_tags.include?("ActiveJob")
end
- class LogSubscriber < ActiveSupport::LogSubscriber
+ class LogSubscriber < ActiveSupport::LogSubscriber #:nodoc:
def enqueue(event)
info do
job = event.payload[:job]
diff --git a/activejob/lib/active_job/queue_adapters.rb b/activejob/lib/active_job/queue_adapters.rb
index 345b01ef00..e865c901ce 100644
--- a/activejob/lib/active_job/queue_adapters.rb
+++ b/activejob/lib/active_job/queue_adapters.rb
@@ -1,4 +1,38 @@
module ActiveJob
+ # == Active Job adapters
+ #
+ # Active Job has adapters for the following queueing backends:
+ #
+ # * {Backburner}[https://github.com/nesquena/backburner]
+ # * {Delayed Job}[https://github.com/collectiveidea/delayed_job]
+ # * {Qu}[https://github.com/bkeepers/qu]
+ # * {Que}[https://github.com/chanks/que]
+ # * {QueueClassic 2.x}[https://github.com/ryandotsmith/queue_classic/tree/v2.2.3]
+ # * {Resque 1.x}[https://github.com/resque/resque/tree/1-x-stable]
+ # * {Sidekiq}[http://sidekiq.org]
+ # * {Sneakers}[https://github.com/jondot/sneakers]
+ # * {Sucker Punch}[https://github.com/brandonhilkert/sucker_punch]
+ #
+ # #### Backends Features
+ #
+ # | | Async | Queues | Delayed | Priorities | Timeout | Retries |
+ # |-------------------|-------|--------|-----------|------------|---------|---------|
+ # | Backburner | Yes | Yes | Yes | Yes | Job | Global |
+ # | Delayed Job | Yes | Yes | Yes | Job | Global | Global |
+ # | Que | Yes | Yes | Yes | Job | No | Job |
+ # | Queue Classic | Yes | Yes | No* | No | No | No |
+ # | Resque | Yes | Yes | Yes (Gem) | Queue | Global | Yes |
+ # | Sidekiq | Yes | Yes | Yes | Queue | No | Job |
+ # | 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 |
+ #
+ # NOTE:
+ # Queue Classic does not support Job scheduling. However you can implement this
+ # yourself or you can use the queue_classic-later gem. See the documentation for
+ # ActiveJob::QueueAdapters::QueueClassicAdapter.
+ #
module QueueAdapters
extend ActiveSupport::Autoload
diff --git a/activejob/lib/active_job/queue_adapters/backburner_adapter.rb b/activejob/lib/active_job/queue_adapters/backburner_adapter.rb
index e1b00f1de7..2453d065de 100644
--- a/activejob/lib/active_job/queue_adapters/backburner_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/backburner_adapter.rb
@@ -2,19 +2,29 @@ require 'backburner'
module ActiveJob
module QueueAdapters
+ # == Backburner adapter for Active Job
+ #
+ # Backburner is a beanstalkd-powered job queue that can handle a very
+ # high volume of jobs. You create background jobs and place them on
+ # multiple work queues to be processed later. Read more about
+ # Backburner {here}[https://github.com/nesquena/backburner].
+ #
+ # To use Backburner set the queue_adapter config to +:backburner+.
+ #
+ # Rails.application.config.active_job.queue_adapter = :backburner
class BackburnerAdapter
class << self
- def enqueue(job)
+ def enqueue(job) #:nodoc:
Backburner::Worker.enqueue JobWrapper, [ job.serialize ], queue: job.queue_name
end
- def enqueue_at(job, timestamp)
+ def enqueue_at(job, timestamp) #:nodoc:
delay = timestamp - Time.current.to_f
Backburner::Worker.enqueue JobWrapper, [ job.serialize ], queue: job.queue_name, delay: delay
end
end
- class JobWrapper
+ class JobWrapper #:nodoc:
class << self
def perform(job_data)
Base.execute job_data
diff --git a/activejob/lib/active_job/queue_adapters/delayed_job_adapter.rb b/activejob/lib/active_job/queue_adapters/delayed_job_adapter.rb
index 658799edfc..370a1fc01f 100644
--- a/activejob/lib/active_job/queue_adapters/delayed_job_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/delayed_job_adapter.rb
@@ -2,18 +2,28 @@ require 'delayed_job'
module ActiveJob
module QueueAdapters
+ # == Delayed Job adapter for Active Job
+ #
+ # Delayed::Job (or DJ) encapsulates the common pattern of asynchronously
+ # executing longer tasks in the background. Although DJ can have many
+ # storage backends one of the most used is based on Active Record.
+ # Read more about Delayed Job {here}[https://github.com/collectiveidea/delayed_job].
+ #
+ # To use Delayed Job set the queue_adapter config to +:delayed_job+.
+ #
+ # Rails.application.config.active_job.queue_adapter = :delayed_job
class DelayedJobAdapter
class << self
- def enqueue(job)
+ def enqueue(job) #:nodoc:
JobWrapper.new.delay(queue: job.queue_name).perform(job.serialize)
end
- def enqueue_at(job, timestamp)
+ def enqueue_at(job, timestamp) #:nodoc:
JobWrapper.new.delay(queue: job.queue_name, run_at: Time.at(timestamp)).perform(job.serialize)
end
end
- class JobWrapper
+ class JobWrapper #:nodoc:
def perform(job_data)
Base.execute(job_data)
end
diff --git a/activejob/lib/active_job/queue_adapters/inline_adapter.rb b/activejob/lib/active_job/queue_adapters/inline_adapter.rb
index fdefa38d5e..e498454909 100644
--- a/activejob/lib/active_job/queue_adapters/inline_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/inline_adapter.rb
@@ -1,12 +1,20 @@
module ActiveJob
module QueueAdapters
+ # == Active Job Inline adapter
+ #
+ # When enqueueing jobs with the Inline adapter the job will be executed
+ # immediately.
+ #
+ # To use the Inline set the queue_adapter config to +:inline+.
+ #
+ # Rails.application.config.active_job.queue_adapter = :inline
class InlineAdapter
class << self
- def enqueue(job)
+ def enqueue(job) #:nodoc:
Base.execute(job.serialize)
end
- def enqueue_at(*)
+ def enqueue_at(*) #:nodoc:
raise NotImplementedError.new("Use a queueing backend to enqueue jobs in the future. Read more at https://github.com/rails/activejob")
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 f681fd7e8a..7cdefefc02 100644
--- a/activejob/lib/active_job/queue_adapters/qu_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/qu_adapter.rb
@@ -2,20 +2,33 @@ require 'qu'
module ActiveJob
module QueueAdapters
+ # == Qu adapter for Active Job
+ #
+ # Qu is a Ruby library for queuing and processing background jobs. It is
+ # heavily inspired by delayed_job and Resque. Qu was created to overcome
+ # some shortcomings in the existing queuing libraries that we experienced.
+ # The advantages of Qu are: Multiple backends (redis, mongo), jobs are
+ # requeued when worker is killed, resque-like API.
+ #
+ # Read more about Que {here}[https://github.com/bkeepers/qu].
+ #
+ # To use Qu set the queue_adapter config to +:qu+.
+ #
+ # Rails.application.config.active_job.queue_adapter = :qu
class QuAdapter
class << self
- def enqueue(job, *args)
+ def enqueue(job, *args) #:nodoc:
Qu::Payload.new(klass: JobWrapper, args: [job.serialize]).tap do |payload|
payload.instance_variable_set(:@queue, job.queue_name)
end.push
end
- def enqueue_at(job, timestamp, *args)
+ def enqueue_at(job, timestamp, *args) #:nodoc:
raise NotImplementedError
end
end
- class JobWrapper < Qu::Job
+ class JobWrapper < Qu::Job #:nodoc:
def initialize(job_data)
@job_data = job_data
end
diff --git a/activejob/lib/active_job/queue_adapters/que_adapter.rb b/activejob/lib/active_job/queue_adapters/que_adapter.rb
index 51891ab07b..e501fe0368 100644
--- a/activejob/lib/active_job/queue_adapters/que_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/que_adapter.rb
@@ -2,18 +2,30 @@ require 'que'
module ActiveJob
module QueueAdapters
+ # == Que adapter for Active Job
+ #
+ # Que is a high-performance alternative to DelayedJob or QueueClassic that
+ # improves the reliability of your application by protecting your jobs with
+ # the same ACID guarantees as the rest of your data. Que is a queue for
+ # Ruby and PostgreSQL that manages jobs using advisory locks.
+ #
+ # Read more about Que {here}[https://github.com/chanks/que].
+ #
+ # To use Que set the queue_adapter config to +:que+.
+ #
+ # Rails.application.config.active_job.queue_adapter = :que
class QueAdapter
class << self
- def enqueue(job)
+ def enqueue(job) #:nodoc:
JobWrapper.enqueue job.serialize, queue: job.queue_name
end
- def enqueue_at(job, timestamp)
+ def enqueue_at(job, timestamp) #:nodoc:
JobWrapper.enqueue job.serialize, queue: job.queue_name, run_at: Time.at(timestamp)
end
end
- class JobWrapper < Que::Job
+ class JobWrapper < Que::Job #:nodoc:
def run(job_data)
Base.execute job_data
end
diff --git a/activejob/lib/active_job/queue_adapters/queue_classic_adapter.rb b/activejob/lib/active_job/queue_adapters/queue_classic_adapter.rb
index ddcc868317..bde20e037e 100644
--- a/activejob/lib/active_job/queue_adapters/queue_classic_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/queue_classic_adapter.rb
@@ -2,13 +2,27 @@ require 'queue_classic'
module ActiveJob
module QueueAdapters
+ # == Queue Classic adapter for Active Job
+ #
+ # queue_classic provides a simple interface to a PostgreSQL-backed message
+ # queue. queue_classic specializes in concurrent locking and minimizing
+ # database load while providing a simple, intuitive developer experience.
+ # queue_classic assumes that you are already using PostgreSQL in your
+ # production environment and that adding another dependency (e.g. redis,
+ # beanstalkd, 0mq) is undesirable.
+ #
+ # Read more about Queue Classic {here}[https://github.com/ryandotsmith/queue_classic].
+ #
+ # To use Queue Classic set the queue_adapter config to +:queue_classic+.
+ #
+ # Rails.application.config.active_job.queue_adapter = :queue_classic
class QueueClassicAdapter
class << self
- def enqueue(job)
+ def enqueue(job) #:nodoc:
build_queue(job.queue_name).enqueue("#{JobWrapper.name}.perform", job.serialize)
end
- def enqueue_at(job, timestamp)
+ def enqueue_at(job, timestamp) #:nodoc:
queue = build_queue(job.queue_name)
unless queue.respond_to?(:enqueue_at)
raise NotImplementedError, 'To be able to schedule jobs with Queue Classic ' \
@@ -28,7 +42,7 @@ module ActiveJob
end
end
- class JobWrapper
+ class JobWrapper #:nodoc:
class << self
def perform(job_data)
Base.execute job_data
diff --git a/activejob/lib/active_job/queue_adapters/resque_adapter.rb b/activejob/lib/active_job/queue_adapters/resque_adapter.rb
index affa3bdfbc..88c6b48fef 100644
--- a/activejob/lib/active_job/queue_adapters/resque_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/resque_adapter.rb
@@ -14,13 +14,24 @@ end
module ActiveJob
module QueueAdapters
+ # == Resque adapter for Active Job
+ #
+ # Resque (pronounced like "rescue") is a Redis-backed library for creating
+ # background jobs, placing those jobs on multiple queues, and processing
+ # them later.
+ #
+ # Read more about Resque {here}[https://github.com/resque/resque].
+ #
+ # To use Resque set the queue_adapter config to +:resque+.
+ #
+ # Rails.application.config.active_job.queue_adapter = :resque
class ResqueAdapter
class << self
- def enqueue(job)
+ def enqueue(job) #:nodoc:
Resque.enqueue_to job.queue_name, JobWrapper, job.serialize
end
- def enqueue_at(job, timestamp)
+ def enqueue_at(job, timestamp) #:nodoc:
unless Resque.respond_to?(:enqueue_at_with_queue)
raise NotImplementedError, "To be able to schedule jobs with Resque you need the " \
"resque-scheduler gem. Please add it to your Gemfile and run bundle install"
@@ -29,7 +40,7 @@ module ActiveJob
end
end
- class JobWrapper
+ class JobWrapper #:nodoc:
class << self
def perform(job_data)
Base.execute job_data
diff --git a/activejob/lib/active_job/queue_adapters/sidekiq_adapter.rb b/activejob/lib/active_job/queue_adapters/sidekiq_adapter.rb
index 79926a1e24..74655cf0ca 100644
--- a/activejob/lib/active_job/queue_adapters/sidekiq_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/sidekiq_adapter.rb
@@ -2,9 +2,21 @@ require 'sidekiq'
module ActiveJob
module QueueAdapters
+ # == Sidekiq adapter for Active Job
+ #
+ # Simple, efficient background processing for Ruby. Sidekiq uses threads to
+ # handle many jobs at the same time in the same process. It does not
+ # require Rails but will integrate tightly with Rails 3/4 to make
+ # background processing dead simple.
+ #
+ # Read more about Sidekiq {here}[http://sidekiq.org].
+ #
+ # To use Sidekiq set the queue_adapter config to +:sidekiq+.
+ #
+ # Rails.application.config.active_job.queue_adapter = :sidekiq
class SidekiqAdapter
class << self
- def enqueue(job)
+ def enqueue(job) #:nodoc:
#Sidekiq::Client does not support symbols as keys
Sidekiq::Client.push \
'class' => JobWrapper,
@@ -13,7 +25,7 @@ module ActiveJob
'retry' => true
end
- def enqueue_at(job, timestamp)
+ def enqueue_at(job, timestamp) #:nodoc:
Sidekiq::Client.push \
'class' => JobWrapper,
'queue' => job.queue_name,
@@ -23,7 +35,7 @@ module ActiveJob
end
end
- class JobWrapper
+ class JobWrapper #:nodoc:
include Sidekiq::Worker
def perform(job_data)
diff --git a/activejob/lib/active_job/queue_adapters/sneakers_adapter.rb b/activejob/lib/active_job/queue_adapters/sneakers_adapter.rb
index 1ab0a87485..6d60a2f303 100644
--- a/activejob/lib/active_job/queue_adapters/sneakers_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/sneakers_adapter.rb
@@ -3,23 +3,35 @@ require 'thread'
module ActiveJob
module QueueAdapters
+ # == Sneakers adapter for Active Job
+ #
+ # A high-performance RabbitMQ background processing framework for Ruby.
+ # Sneakers is being used in production for both I/O and CPU intensive
+ # workloads, and have achieved the goals of high-performance and
+ # 0-maintenance, as designed.
+ #
+ # Read more about Sneakers {here}[https://github.com/jondot/sneakers].
+ #
+ # To use Sneakers set the queue_adapter config to +:sneakers+.
+ #
+ # Rails.application.config.active_job.queue_adapter = :sneakers
class SneakersAdapter
@monitor = Monitor.new
class << self
- def enqueue(job)
+ def enqueue(job) #:nodoc:
@monitor.synchronize do
JobWrapper.from_queue job.queue_name
JobWrapper.enqueue ActiveSupport::JSON.encode(job.serialize)
end
end
- def enqueue_at(job, timestamp)
+ def enqueue_at(job, timestamp) #:nodoc:
raise NotImplementedError
end
end
- class JobWrapper
+ class JobWrapper #:nodoc:
include Sneakers::Worker
from_queue 'default'
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 b19a38093f..be9e7fd03a 100644
--- a/activejob/lib/active_job/queue_adapters/sucker_punch_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/sucker_punch_adapter.rb
@@ -2,18 +2,33 @@ require 'sucker_punch'
module ActiveJob
module QueueAdapters
+ # == Sucker Punch adapter for Active Job
+ #
+ # Sucker Punch is a single-process Ruby asynchronous processing library.
+ # It's girl_friday and DSL sugar on top of Celluloid. With Celluloid's
+ # actor pattern, we can do asynchronous processing within a single process.
+ # This reduces costs of hosting on a service like Heroku along with the
+ # memory footprint of having to maintain additional jobs if hosting on
+ # a dedicated server. All queues can run within a single Rails/Sinatra
+ # process.
+ #
+ # Read more about Sucker Punch {here}[https://github.com/brandonhilkert/sucker_punch].
+ #
+ # To use Sucker Punch set the queue_adapter config to +:sucker_punch+.
+ #
+ # Rails.application.config.active_job.queue_adapter = :sucker_punch
class SuckerPunchAdapter
class << self
- def enqueue(job)
+ def enqueue(job) #:nodoc:
JobWrapper.new.async.perform job.serialize
end
- def enqueue_at(job, timestamp)
+ def enqueue_at(job, timestamp) #:nodoc:
raise NotImplementedError
end
end
- class JobWrapper
+ class JobWrapper #:nodoc:
include SuckerPunch::Job
def perform(job_data)
diff --git a/activejob/lib/active_job/queue_adapters/test_adapter.rb b/activejob/lib/active_job/queue_adapters/test_adapter.rb
index b9997efddf..e4fdf60008 100644
--- a/activejob/lib/active_job/queue_adapters/test_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/test_adapter.rb
@@ -1,52 +1,31 @@
module ActiveJob
module QueueAdapters
+ # == Test adapter for Active Job
+ #
+ # The test adapter should be used only in testing. Along with
+ # <tt>ActiveJob::TestCase</tt> and <tt>ActiveJob::TestHelper</tt>
+ # it makes a great tool to test your Rails application.
+ #
+ # To use the test adapter set queue_adapter config to +:test+.
+ #
+ # Rails.application.config.active_job.queue_adapter = :test
class TestAdapter
- attr_accessor(:perform_enqueued_jobs) { false }
- attr_accessor(:perform_enqueued_at_jobs) { false }
delegate :name, to: :class
+ attr_accessor(:perform_enqueued_jobs, :perform_enqueued_at_jobs)
+ attr_writer(:enqueued_jobs, :performed_jobs)
# Provides a store of all the enqueued jobs with the TestAdapter so you can check them.
def enqueued_jobs
@enqueued_jobs ||= []
end
- # Allows you to overwrite the default enqueued jobs store from an array to some
- # other object. If you just want to clear the store,
- # call ActiveJob::QueueAdapters::TestAdapter.enqueued_jobs.clear.
- #
- # If you place another object here, please make sure it responds to:
- #
- # * << (message)
- # * clear
- # * length
- # * size
- # * and other common Array methods
- def enqueued_jobs=(val)
- @enqueued_jobs = val
- end
-
# Provides a store of all the performed jobs with the TestAdapter so you can check them.
def performed_jobs
@performed_jobs ||= []
end
- # Allows you to overwrite the default performed jobs store from an array to some
- # other object. If you just want to clear the store,
- # call ActiveJob::QueueAdapters::TestAdapter.performed_jobs.clear.
- #
- # If you place another object here, please make sure it responds to:
- #
- # * << (message)
- # * clear
- # * length
- # * size
- # * and other common Array methods
- def performed_jobs=(val)
- @performed_jobs = val
- end
-
- def enqueue(job)
- if perform_enqueued_jobs?
+ def enqueue(job) #:nodoc:
+ if perform_enqueued_jobs
performed_jobs << {job: job.class, args: job.arguments, queue: job.queue_name}
job.perform_now
else
@@ -54,23 +33,14 @@ module ActiveJob
end
end
- def enqueue_at(job, timestamp)
- if perform_enqueued_at_jobs?
+ def enqueue_at(job, timestamp) #:nodoc:
+ if perform_enqueued_at_jobs
performed_jobs << {job: job.class, args: job.arguments, queue: job.queue_name, at: timestamp}
job.perform_now
else
enqueued_jobs << {job: job.class, args: job.arguments, queue: job.queue_name, at: timestamp}
end
end
-
- private
- def perform_enqueued_jobs?
- perform_enqueued_jobs
- end
-
- def perform_enqueued_at_jobs?
- perform_enqueued_at_jobs
- end
end
end
end
diff --git a/activejob/test/cases/argument_serialization_test.rb b/activejob/test/cases/argument_serialization_test.rb
new file mode 100644
index 0000000000..5a46c5cdef
--- /dev/null
+++ b/activejob/test/cases/argument_serialization_test.rb
@@ -0,0 +1,76 @@
+require 'helper'
+require 'active_job/arguments'
+require 'models/person'
+require 'active_support/core_ext/hash/indifferent_access'
+
+class ArgumentSerializationTest < ActiveSupport::TestCase
+ setup do
+ @person = Person.find('5')
+ end
+
+ [ nil, 1, 1.0, 1_000_000_000_000_000_000_000,
+ 'a', true, false,
+ [ 1, 'a' ],
+ { 'a' => 1 }
+ ].each do |arg|
+ test "serializes #{arg.class} verbatim" do
+ assert_arguments_unchanged arg
+ end
+ end
+
+ [ :a, Object.new, self, Person.find('5').to_gid ].each do |arg|
+ test "does not serialize #{arg.class}" do
+ assert_raises ActiveJob::SerializationError do
+ ActiveJob::Arguments.serialize [ arg ]
+ end
+
+ assert_raises ActiveJob::DeserializationError do
+ ActiveJob::Arguments.deserialize [ arg ]
+ end
+ end
+ end
+
+ test 'should convert records to Global IDs' do
+ assert_arguments_roundtrip [@person], [@person.to_gid.to_s]
+ end
+
+ test 'should dive deep into arrays and hashes' do
+ assert_arguments_roundtrip [3, [@person]], [3, [@person.to_gid.to_s]]
+ assert_arguments_roundtrip [{ 'a' => @person }], [{ 'a' => @person.to_gid.to_s }.with_indifferent_access]
+ end
+
+ test 'should stringify symbol hash keys' do
+ assert_equal [ 'a' => 1 ], ActiveJob::Arguments.serialize([ a: 1 ])
+ end
+
+ test 'should disallow non-string/symbol hash keys' do
+ assert_raises ActiveJob::SerializationError do
+ ActiveJob::Arguments.serialize [ { 1 => 2 } ]
+ end
+
+ assert_raises ActiveJob::SerializationError do
+ ActiveJob::Arguments.serialize [ { :a => [{ 2 => 3 }] } ]
+ end
+ end
+
+ test 'should not allow non-primitive objects' do
+ assert_raises ActiveJob::SerializationError do
+ ActiveJob::Arguments.serialize [Object.new]
+ end
+
+ assert_raises ActiveJob::SerializationError do
+ ActiveJob::Arguments.serialize [1, [Object.new]]
+ end
+ end
+
+ private
+ def assert_arguments_unchanged(*args)
+ assert_arguments_roundtrip args, args
+ end
+
+ def assert_arguments_roundtrip(args, expected_serialized_args)
+ serialized = ActiveJob::Arguments.serialize(args)
+ assert_equal expected_serialized_args, serialized
+ assert_equal args, ActiveJob::Arguments.deserialize(serialized)
+ end
+end
diff --git a/activejob/test/cases/parameters_test.rb b/activejob/test/cases/parameters_test.rb
deleted file mode 100644
index 78853c51e1..0000000000
--- a/activejob/test/cases/parameters_test.rb
+++ /dev/null
@@ -1,77 +0,0 @@
-require 'helper'
-require 'active_job/arguments'
-require 'models/person'
-require 'active_support/core_ext/hash/indifferent_access'
-
-class ParameterSerializationTest < ActiveSupport::TestCase
- test 'should make no change to regular values' do
- assert_equal [ 1, "something" ], ActiveJob::Arguments.serialize([ 1, "something" ])
- end
-
- test 'should not allow complex objects' do
- assert_equal [ nil ], ActiveJob::Arguments.serialize([ nil ])
- assert_equal [ 1 ], ActiveJob::Arguments.serialize([ 1 ])
- assert_equal [ 1.0 ], ActiveJob::Arguments.serialize([ 1.0 ])
- assert_equal [ 'a' ], ActiveJob::Arguments.serialize([ 'a' ])
- assert_equal [ true ], ActiveJob::Arguments.serialize([ true ])
- assert_equal [ false ], ActiveJob::Arguments.serialize([ false ])
- assert_equal [ { "a" => 1, "b" => 2 } ], ActiveJob::Arguments.serialize([ { a: 1, "b" => 2 } ])
- assert_equal [ [ 1 ] ], ActiveJob::Arguments.serialize([ [ 1 ] ])
- assert_equal [ 1_000_000_000_000_000_000_000 ], ActiveJob::Arguments.serialize([ 1_000_000_000_000_000_000_000 ])
-
- err = assert_raises ActiveJob::SerializationError do
- ActiveJob::Arguments.serialize([ 1, self ])
- end
- assert_equal "Unsupported argument type: #{self.class.name}", err.message
- end
-
- test 'should dive deep into arrays or hashes' do
- assert_equal [ { "a" => Person.find(5).gid.to_s }.with_indifferent_access ], ActiveJob::Arguments.serialize([ { a: Person.find(5) } ])
- assert_equal [ [ Person.find(5).gid.to_s ] ], ActiveJob::Arguments.serialize([ [ Person.find(5) ] ])
- end
-
- test 'should dive deep into arrays or hashes and raise exception on complex objects' do
- err = assert_raises ActiveJob::SerializationError do
- ActiveJob::Arguments.serialize([ 1, [self] ])
- end
- assert_equal "Unsupported argument type: #{self.class.name}", err.message
- end
-
- test 'shoud dive deep into hashes and allow raise exception on not string/symbol keys' do
- err = assert_raises ActiveJob::SerializationError do
- ActiveJob::Arguments.serialize([ [ { 1 => 2 } ] ])
- end
- assert_equal "Unsupported hash key type: Fixnum", err.message
- end
-
- test 'should serialize records with global id' do
- assert_equal [ Person.find(5).gid.to_s ], ActiveJob::Arguments.serialize([ Person.find(5) ])
- end
-
- test 'should serialize values and records together' do
- assert_equal [ 3, Person.find(5).gid.to_s ], ActiveJob::Arguments.serialize([ 3, Person.find(5) ])
- end
-end
-
-class ParameterDeserializationTest < ActiveSupport::TestCase
- test 'should make no change to regular values' do
- assert_equal [ 1, "something" ], ActiveJob::Arguments.deserialize([ 1, "something" ])
- end
-
- test 'should deserialize records with global id' do
- assert_equal [ Person.find(5) ], ActiveJob::Arguments.deserialize([ Person.find(5).gid ])
- end
-
- test 'should serialize values and records together' do
- assert_equal [ 3, Person.find(5) ], ActiveJob::Arguments.deserialize([ 3, Person.find(5).gid ])
- end
-
- test 'should dive deep when deserialising arrays' do
- assert_equal [ [ 3, Person.find(5) ] ], ActiveJob::Arguments.deserialize([ [ 3, Person.find(5).gid ] ])
- end
-
- test 'should dive deep when deserialising hashes' do
- assert_equal [ { "5" => Person.find(5) } ], ActiveJob::Arguments.deserialize([ { "5" => Person.find(5).gid } ])
- end
-
-end
diff --git a/activejob/test/helper.rb b/activejob/test/helper.rb
index 3386cef6f3..ce22833b11 100644
--- a/activejob/test/helper.rb
+++ b/activejob/test/helper.rb
@@ -27,4 +27,4 @@ end
require 'active_support/testing/autorun'
ActiveJob::Base.logger.level = Logger::DEBUG
-
+ActiveSupport::TestCase.test_order = :random
diff --git a/activemodel/CHANGELOG.md b/activemodel/CHANGELOG.md
index c14b0688c7..5588699d9b 100644
--- a/activemodel/CHANGELOG.md
+++ b/activemodel/CHANGELOG.md
@@ -12,18 +12,21 @@
* Deprecate `reset_#{attribute}` in favor of `restore_#{attribute}`.
- These methods may cause confusion with the `reset_changes` that behaves differently
- of them.
+ These methods may cause confusion with the `reset_changes`, which has
+ different behaviour.
+
+ *Rafael Mendonça França*
* Deprecate `ActiveModel::Dirty#reset_changes` in favor of `#clear_changes_information`.
- This method name is causing confusion with the `reset_#{attribute}`
- methods. While `reset_name` set the value of the name attribute for the
- previous value `reset_changes` only discard the changes and previous
- changes.
+ Method's name is causing confusion with the `reset_#{attribute}` methods.
+ While `reset_name` sets the value of the name attribute to previous value
+ `reset_changes` only discards the changes.
+
+ *Rafael Mendonça França*
-* Added `restore_attributes` method to `ActiveModel::Dirty` API to restore all the
- changed values to the previous data.
+* Added `restore_attributes` method to `ActiveModel::Dirty` API which restores
+ the value of changed attributes to previous value.
*Igor G.*
diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb
index f6ad35769f..8f2a069ba3 100644
--- a/activemodel/lib/active_model/secure_password.rb
+++ b/activemodel/lib/active_model/secure_password.rb
@@ -75,7 +75,7 @@ module ActiveModel
end
validates_length_of :password, maximum: ActiveModel::SecurePassword::MAX_PASSWORD_LENGTH_ALLOWED
- validates_confirmation_of :password, if: ->{ password.present? }
+ validates_confirmation_of :password, allow_blank: true
end
# This code is necessary as long as the protected_attributes gem is supported.
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 13e8292954..c02de2e979 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,7 +1,28 @@
+* Don't autosave unchanged has_one through records.
+
+ *Alan Kennedy*, *Steve Parrington*
+
+* Do not dump foreign keys for ignored tables. *Yves Senn*
+
+* PostgreSQL adapter correctly dumps foreign keys targeting tables
+ outside the schema search path.
+
+ Fixes #16907.
+
+ *Matthew Draper*, *Yves Senn*
+
+* When a thread is killed, rollback the active transaction, instead of
+ committing it during the stack unwind. Previously, we could commit half-
+ completed work. This fix only works for Ruby 2.0+; on 1.9, we can't
+ distinguish a thread kill from an ordinary non-local (block) return, so must
+ default to committing.
+
+ *Chris Hanks*
+
* A `NullRelation` should represent nothing. This fixes a bug where
`Comment.where(post_id: Post.none)` returned a non-empty result.
- Closes #15176.
+ Fixes #15176.
*Matthew Draper*, *Yves Senn*
@@ -22,7 +43,7 @@
*Girish Sonawane*
-* Introduce `connection.supports_views?` to check wether the current adapter
+* Introduce `connection.supports_views?` to check whether the current adapter
has support for SQL views. Connection adapters should define this method.
*Yves Senn*
@@ -70,17 +91,18 @@
*Agis Anastasopoulos*
-* Fixed the `Relation#exists?` to work with polymorphic associations.
+* Fixed `Relation#exists?` to work with polymorphic associations.
Fixes #15821.
*Kassio Borges*
-* Currently, Active Record will rescue any errors raised within
- after_rollback/after_create callbacks and print them to the logs. Next versions of rails
- will not rescue those errors anymore, and just bubble them up, as the other callbacks.
+* Currently, Active Record rescues any errors raised within
+ `after_rollback`/`after_create` callbacks and prints them to the logs.
+ Future versions of Rails will not rescue these errors anymore and
+ just bubble them up like the other callbacks.
- This adds a opt-in flag to enable that behaviour, of not rescuing the errors.
+ This commit adds an opt-in flag to enable not rescuing the errors.
Example:
@@ -104,7 +126,7 @@
*Sean Griffin*
-* Fix regression on after_commit that didnt fire when having nested transactions.
+* Fix regression on `after_commit` that did not fire with nested transactions.
Fixes #16425.
@@ -140,9 +162,9 @@
* Define `id_was` to get the previous value of the primary key.
- Currently when we call id_was and we have a custom primary key name
+ Currently when we call `id_was` and we have a custom primary key name,
Active Record will return the current value of the primary key. This
- make impossible to correctly do an update operation if you change the
+ makes it impossible to correctly do an update operation if you change the
id.
Fixes #16413.
@@ -174,7 +196,7 @@
*Eileen M. Uchtitelle*, *Aaron Patterson*
-* No verbose backtrace by db:drop when database does not exist.
+* No verbose backtrace by `db:drop` when database does not exist.
Fixes #16295.
@@ -219,7 +241,7 @@
*Stefan Kanev*
-* Dont swallow errors on compute_type when having a bad alias_method on
+* Do not swallow errors on `compute_type` when having a bad `alias_method` on
a class.
*arthurnn*
@@ -302,7 +324,7 @@
*Eileen M. Uchitelle, Aaron Patterson*
-* Avoid type casting boolean and ActiveSupport::Duration values to numeric
+* Avoid type casting boolean and `ActiveSupport::Duration` values to numeric
values for string columns. Otherwise, in some database, the string column
values will be coerced to a numeric allowing false or 0.seconds match any
string starting with a non-digit.
@@ -333,8 +355,8 @@
*Abdelkader Boudih*
-* Move 'dependent: :destroy' handling for 'belongs_to'
- from 'before_destroy' to 'after_destroy' callback chain
+* Move 'dependent: :destroy' handling for `belongs_to`
+ from `before_destroy` to `after_destroy` callback chain
Fixes #12380.
@@ -342,8 +364,9 @@
* Detect in-place modifications on String attributes.
- Before this change user have to mark the attribute as changed to it be persisted
- in the database. Now it is not required anymore.
+ Before this change, an attribute modified in-place had to be marked as
+ changed in order for it to be persisted in the database. Now it is no longer
+ required.
Before:
@@ -455,7 +478,7 @@
*Sean Griffin*
-* Pluck now works when selecting columns from different tables with the same
+* `Pluck` now works when selecting columns from different tables with the same
name.
Fixes #15649.
@@ -522,7 +545,7 @@
*Sean Griffin*
-* Implemented ActiveRecord::Base#pretty_print to work with PP.
+* Implemented `ActiveRecord::Base#pretty_print` to work with PP.
*Ethan*
@@ -563,16 +586,16 @@
*Arun Agrawal*
-* Fix redefine a has_and_belongs_to_many inside inherited class
- Fixing regression case, where redefining the same has_an_belongs_to_many
+* Fix redefine a `has_and_belongs_to_many` inside inherited class
+ Fixing regression case, where redefining the same `has_and_belongs_to_many`
definition into a subclass would raise.
Fixes #14983.
*arthurnn*
-* Fix has_and_belongs_to_many public reflection.
- When defining a has_and_belongs_to_many, internally we convert that to two has_many.
+* Fix `has_and_belongs_to_many` public reflection.
+ When defining a `has_and_belongs_to_many`, internally we convert that to two has_many.
But as `reflections` is a public API, people expect to see the right macro.
Fixes #14682.
@@ -609,7 +632,7 @@
*Brock Trappitt*
-* Fixed the inferred table name of a has_and_belongs_to_many auxiliar
+* Fixed the inferred table name of a `has_and_belongs_to_many` auxiliar
table inside a schema.
Fixes #14824.
@@ -653,7 +676,7 @@
*Aaron Nelson*
-* Fix how to calculate associated class name when using namespaced has_and_belongs_to_many
+* Fix how to calculate associated class name when using namespaced `has_and_belongs_to_many`
association.
Fixes #14709.
@@ -723,7 +746,7 @@
* Fixed has_and_belongs_to_many's CollectionAssociation size calculation.
- has_and_belongs_to_many should fall back to using the normal CollectionAssociation's
+ `has_and_belongs_to_many` should fall back to using the normal CollectionAssociation's
size calculation if the collection is not cached or loaded.
Fixes #14913, #14914.
@@ -783,7 +806,7 @@
*Timur Alperovich*
-* Give ActiveRecord::PredicateBuilder private methods the privacy they deserve.
+* Give `ActiveRecord::PredicateBuilder` private methods the privacy they deserve.
*Hector Satre*
@@ -839,7 +862,7 @@
*Kuldeep Aggarwal*
-* Fixed has_many association to make it support irregular inflections.
+* Fixed `has_many` association to make it support irregular inflections.
Fixes #8928.
@@ -953,7 +976,7 @@
* `to_sql` on an association now matches the query that is actually executed, where it
could previously have incorrectly accrued additional conditions (e.g. as a result of
- a previous query). CollectionProxy now always defers to the association scope's
+ a previous query). `CollectionProxy` now always defers to the association scope's
`arel` method so the (incorrect) inherited one should be entirely concealed.
Fixes #14003.
@@ -1184,7 +1207,7 @@
The current solution of incrementing the beginning is not correct and is now
deprecated. For subtypes where we don't know how to increment (e.g. `#succ`
- is not defined) it will raise an ArgumentException for ranges with excluding
+ is not defined) it will raise an `ArgumentException` for ranges with excluding
beginnings.
*Yves Senn*
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index 36d0d63c71..f4a4e3f605 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -111,7 +111,7 @@ module ActiveRecord
# # => false
def instance_method_already_implemented?(method_name)
if dangerous_attribute_method?(method_name)
- raise DangerousAttributeError, "#{method_name} is defined by Active Record"
+ raise DangerousAttributeError, "#{method_name} is defined by Active Record. Check to make sure that you don't have an attribute or method with the same name."
end
if superclass == Base
diff --git a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
index f439bd1ffe..b7fe079ef5 100644
--- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
+++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
@@ -2,6 +2,8 @@ module ActiveRecord
module AttributeMethods
module TimeZoneConversion
class TimeZoneConverter < SimpleDelegator # :nodoc:
+ include Type::Decorator
+
def type_cast_from_database(value)
convert_time_to_time_zone(super)
end
diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb
index a8e4d25df2..c384e8c413 100644
--- a/activerecord/lib/active_record/autosave_association.rb
+++ b/activerecord/lib/active_record/autosave_association.rb
@@ -403,7 +403,9 @@ module ActiveRecord
# If the record is new or it has changed, returns true.
def record_changed?(reflection, record, key)
- record.new_record? || record[reflection.foreign_key] != key || record.attribute_changed?(reflection.foreign_key)
+ record.new_record? ||
+ (record.has_attribute?(reflection.foreign_key) && record[reflection.foreign_key] != key) ||
+ record.attribute_changed?(reflection.foreign_key)
end
# Saves the associated record if it's new or <tt>:autosave</tt> is enabled.
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb
index 90be835d8a..fd666c8c39 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb
@@ -190,11 +190,17 @@ module ActiveRecord
rollback_transaction if transaction
raise
ensure
- begin
- commit_transaction unless error
- rescue Exception
- transaction.rollback unless transaction.state.completed?
- raise
+ unless error
+ if Thread.current.status == 'aborting'
+ rollback_transaction
+ else
+ begin
+ commit_transaction
+ rescue Exception
+ transaction.rollback unless transaction.state.completed?
+ raise
+ end
+ end
end
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 767b6b614a..799aafbd99 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
@@ -60,8 +60,8 @@ module ActiveRecord
def create_database(name, options = {})
options = { encoding: 'utf8' }.merge!(options.symbolize_keys)
- option_string = options.sum do |key, value|
- case key
+ option_string = options.inject("") do |memo, (key, value)|
+ memo += case key
when :owner
" OWNER = \"#{value}\""
when :template
@@ -466,7 +466,7 @@ module ActiveRecord
def foreign_keys(table_name)
fk_info = select_all <<-SQL.strip_heredoc
- SELECT t2.relname AS to_table, a1.attname AS column, a2.attname AS primary_key, c.conname AS name, c.confupdtype AS on_update, c.confdeltype AS on_delete
+ SELECT t2.oid::regclass::text AS to_table, a1.attname AS column, a2.attname AS primary_key, c.conname AS name, c.confupdtype AS on_update, c.confdeltype AS on_delete
FROM pg_constraint c
JOIN pg_class t1 ON c.conrelid = t1.oid
JOIN pg_class t2 ON c.confrelid = t2.oid
@@ -488,6 +488,7 @@ module ActiveRecord
options[:on_delete] = extract_foreign_key_action(row['on_delete'])
options[:on_update] = extract_foreign_key_action(row['on_update'])
+
ForeignKeyDefinition.new(table_name, row['to_table'], options)
end
end
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index 83859e474a..069aa977bf 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -124,6 +124,8 @@ module ActiveRecord
def find(*ids)
# We don't have cache keys for this stuff yet
return super unless ids.length == 1
+ # Allow symbols to super to maintain compatibility for deprecated finders until Rails 5
+ return super if ids.first.kind_of?(Symbol)
return super if block_given? ||
primary_key.nil? ||
default_scopes.any? ||
@@ -160,6 +162,9 @@ module ActiveRecord
v.nil? || Array === v || Hash === v
}
+ # We can't cache Post.find_by(author: david) ...yet
+ return super unless hash.keys.all? { |k| columns_hash.has_key?(k.to_s) }
+
key = hash.keys
klass = self
@@ -179,7 +184,7 @@ module ActiveRecord
end
def find_by!(*args)
- find_by(*args) or raise RecordNotFound
+ find_by(*args) or raise RecordNotFound.new("Couldn't find #{name}")
end
def initialize_generated_modules
diff --git a/activerecord/lib/active_record/counter_cache.rb b/activerecord/lib/active_record/counter_cache.rb
index f0b6afc4b4..73fd96f979 100644
--- a/activerecord/lib/active_record/counter_cache.rb
+++ b/activerecord/lib/active_record/counter_cache.rb
@@ -20,7 +20,7 @@ module ActiveRecord
def reset_counters(id, *counters)
object = find(id)
counters.each do |counter_association|
- has_many_association = _reflect_on_association(counter_association.to_sym)
+ has_many_association = _reflect_on_association(counter_association)
unless has_many_association
has_many = reflect_on_all_associations(:has_many)
has_many_association = has_many.find { |association| association.counter_cache_column && association.counter_cache_column.to_sym == counter_association.to_sym }
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index 4527452f1a..1724fa893d 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -181,6 +181,9 @@ module ActiveRecord
# * Stable, autogenerated IDs
# * Label references for associations (belongs_to, has_one, has_many)
# * HABTM associations as inline lists
+ #
+ # There are some more advanced features available even if the id is specified:
+ #
# * Autofilled timestamp columns
# * Fixture label interpolation
# * Support for YAML defaults
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index 90e99957f6..eaaa409636 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -19,15 +19,15 @@ module ActiveRecord
#
# Person.group(:city).count
# # => { 'Rome' => 5, 'Paris' => 3 }
- #
- # If +count+ is used with +group+ for multiple columns, it returns a Hash whose
- # keys are an array containing the individual values of each column and the value
+ #
+ # If +count+ is used with +group+ for multiple columns, it returns a Hash whose
+ # keys are an array containing the individual values of each column and the value
# of each key would be the +count+.
- #
+ #
# Article.group(:status, :category).count
- # # => {["draft", "business"]=>10, ["draft", "technology"]=>4,
+ # # => {["draft", "business"]=>10, ["draft", "technology"]=>4,
# ["published", "business"]=>0, ["published", "technology"]=>2}
- #
+ #
# If +count+ is used with +select+, it will count the selected columns:
#
# Person.select(:age).count
@@ -274,7 +274,7 @@ module ActiveRecord
group_attrs = group_values
if group_attrs.first.respond_to?(:to_sym)
- association = @klass._reflect_on_association(group_attrs.first.to_sym)
+ association = @klass._reflect_on_association(group_attrs.first)
associated = group_attrs.size == 1 && association && association.belongs_to? # only count belongs_to associations
group_fields = Array(associated ? association.foreign_key : group_attrs)
else
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index b1753b27e1..ed56369f86 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -103,7 +103,7 @@ module ActiveRecord
# Same as +take+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
# is found. Note that <tt>take!</tt> accepts no arguments.
def take!
- take or raise RecordNotFound
+ take or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]")
end
# Find the first record (or first N records if a parameter is supplied).
@@ -138,7 +138,7 @@ module ActiveRecord
# Same as +first+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
# is found. Note that <tt>first!</tt> accepts no arguments.
def first!
- first or raise RecordNotFound
+ first or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]")
end
# Find the last record (or last N records if a parameter is supplied).
@@ -171,7 +171,7 @@ module ActiveRecord
# Same as +last+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
# is found. Note that <tt>last!</tt> accepts no arguments.
def last!
- last or raise RecordNotFound
+ last or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]")
end
# Find the second record.
@@ -187,7 +187,7 @@ module ActiveRecord
# Same as +second+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
# is found.
def second!
- second or raise RecordNotFound
+ second or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]")
end
# Find the third record.
@@ -203,7 +203,7 @@ module ActiveRecord
# Same as +third+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
# is found.
def third!
- third or raise RecordNotFound
+ third or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]")
end
# Find the fourth record.
@@ -219,7 +219,7 @@ module ActiveRecord
# Same as +fourth+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
# is found.
def fourth!
- fourth or raise RecordNotFound
+ fourth or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]")
end
# Find the fifth record.
@@ -235,7 +235,7 @@ module ActiveRecord
# Same as +fifth+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
# is found.
def fifth!
- fifth or raise RecordNotFound
+ fifth or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]")
end
# Find the forty-second record. Also known as accessing "the reddit".
@@ -251,7 +251,7 @@ module ActiveRecord
# Same as +forty_two+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
# is found.
def forty_two!
- forty_two or raise RecordNotFound
+ forty_two or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]")
end
# Returns +true+ if a record exists in the table that matches the +id+ or
diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb
index eff5c8f09c..3df0df40ee 100644
--- a/activerecord/lib/active_record/relation/predicate_builder.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder.rb
@@ -26,7 +26,7 @@ module ActiveRecord
queries << '1=0'
else
table = Arel::Table.new(column, default_table.engine)
- association = klass._reflect_on_association(column.to_sym)
+ association = klass._reflect_on_association(column)
value.each do |k, v|
queries.concat expand(association && association.klass, table, k, v)
@@ -55,7 +55,7 @@ module ActiveRecord
#
# For polymorphic relationships, find the foreign key and type:
# PriceEstimate.where(estimate_of: treasure)
- if klass && reflection = klass._reflect_on_association(column.to_sym)
+ if klass && reflection = klass._reflect_on_association(column)
if reflection.polymorphic? && base_class = polymorphic_base_class_from_value(value)
queries << build(table[reflection.foreign_type], base_class)
end
diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb
index 68d976c240..82c5ca291c 100644
--- a/activerecord/lib/active_record/schema_dumper.rb
+++ b/activerecord/lib/active_record/schema_dumper.rb
@@ -100,7 +100,7 @@ HEADER
# dump foreign keys at the end to make sure all dependent tables exist.
if @connection.supports_foreign_keys?
sorted_tables.each do |tbl|
- foreign_keys(tbl, stream)
+ foreign_keys(tbl, stream) unless ignored?(tbl)
end
end
end
diff --git a/activerecord/lib/active_record/type.rb b/activerecord/lib/active_record/type.rb
index f1384e0bb2..e3d6c5957e 100644
--- a/activerecord/lib/active_record/type.rb
+++ b/activerecord/lib/active_record/type.rb
@@ -1,3 +1,4 @@
+require 'active_record/type/decorator'
require 'active_record/type/mutable'
require 'active_record/type/numeric'
require 'active_record/type/time_value'
diff --git a/activerecord/lib/active_record/type/decorator.rb b/activerecord/lib/active_record/type/decorator.rb
new file mode 100644
index 0000000000..9fce38ea44
--- /dev/null
+++ b/activerecord/lib/active_record/type/decorator.rb
@@ -0,0 +1,14 @@
+module ActiveRecord
+ module Type
+ module Decorator # :nodoc:
+ def init_with(coder)
+ @subtype = coder['subtype']
+ __setobj__(@subtype)
+ end
+
+ def encode_with(coder)
+ coder['subtype'] = __getobj__
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/type/serialized.rb b/activerecord/lib/active_record/type/serialized.rb
index 5b512433b0..17004b3593 100644
--- a/activerecord/lib/active_record/type/serialized.rb
+++ b/activerecord/lib/active_record/type/serialized.rb
@@ -2,6 +2,7 @@ module ActiveRecord
module Type
class Serialized < SimpleDelegator # :nodoc:
include Mutable
+ include Decorator
attr_reader :subtype, :coder
@@ -36,14 +37,13 @@ module ActiveRecord
end
def init_with(coder)
- @subtype = coder['subtype']
@coder = coder['coder']
- __setobj__(@subtype)
+ super
end
def encode_with(coder)
- coder['subtype'] = @subtype
coder['coder'] = @coder
+ super
end
private
diff --git a/activerecord/test/cases/adapters/postgresql/schema_test.rb b/activerecord/test/cases/adapters/postgresql/schema_test.rb
index 9e5fd17dc4..6f40b0d2de 100644
--- a/activerecord/test/cases/adapters/postgresql/schema_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/schema_test.rb
@@ -1,4 +1,5 @@
require "cases/helper"
+require 'support/schema_dumping_helper'
class SchemaTest < ActiveRecord::TestCase
self.use_transactional_fixtures = false
@@ -426,3 +427,28 @@ class SchemaTest < ActiveRecord::TestCase
assert_equal this_index_name, this_index.name
end
end
+
+class SchemaForeignKeyTest < ActiveRecord::TestCase
+ include SchemaDumpingHelper
+
+ setup do
+ @connection = ActiveRecord::Base.connection
+ end
+
+ def test_dump_foreign_key_targeting_different_schema
+ @connection.create_schema "my_schema"
+ @connection.create_table "my_schema.trains" do |t|
+ t.string :name
+ end
+ @connection.create_table "wagons" do |t|
+ t.integer :train_id
+ end
+ @connection.add_foreign_key "wagons", "my_schema.trains", column: "train_id"
+ output = dump_table_schema "wagons"
+ assert_match %r{\s+add_foreign_key "wagons", "my_schema.trains", column: "train_id"$}, output
+ ensure
+ @connection.execute "DROP TABLE IF EXISTS wagons"
+ @connection.execute "DROP TABLE IF EXISTS my_schema.trains"
+ @connection.execute "DROP SCHEMA IF EXISTS my_schema"
+ end
+end
diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb
index b4917e727a..7ca7349528 100644
--- a/activerecord/test/cases/attribute_methods_test.rb
+++ b/activerecord/test/cases/attribute_methods_test.rb
@@ -681,6 +681,14 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
+ def test_yaml_dumping_record_with_time_zone_aware_attribute
+ in_time_zone "Pacific Time (US & Canada)" do
+ record = Topic.new(id: 1)
+ record.written_on = "Jan 01 00:00:00 2014"
+ assert_equal record, YAML.load(YAML.dump(record))
+ end
+ end
+
def test_setting_time_zone_conversion_for_attributes_should_write_value_on_class_variable
Topic.skip_time_zone_conversion_for_attributes = [:field_a]
Minimalistic.skip_time_zone_conversion_for_attributes = [:field_b]
@@ -726,13 +734,13 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_raise(ActiveRecord::UnknownAttributeError) { @target.new.attributes = { :title => "Ants in pants" } }
end
- def test_bulk_update_raise_unknown_attribute_errro
+ def test_bulk_update_raise_unknown_attribute_error
error = assert_raises(ActiveRecord::UnknownAttributeError) {
@target.new(:hello => "world")
}
- assert @target, error.record
- assert "hello", error.attribute
- assert "unknown attribute: hello", error.message
+ assert_instance_of @target, error.record
+ assert_equal "hello", error.attribute
+ assert_equal "unknown attribute: hello", error.message
end
def test_methods_override_in_multi_level_subclass
diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb
index 025cdbeba9..b2a7d3956d 100644
--- a/activerecord/test/cases/autosave_association_test.rb
+++ b/activerecord/test/cases/autosave_association_test.rb
@@ -19,6 +19,9 @@ require 'models/treasure'
require 'models/eye'
require 'models/electron'
require 'models/molecule'
+require 'models/member'
+require 'models/member_detail'
+require 'models/organization'
class TestAutosaveAssociationsInGeneral < ActiveRecord::TestCase
def test_autosave_validation
@@ -1116,6 +1119,27 @@ class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
end
end
+class TestAutosaveAssociationOnAHasOneThroughAssociation < ActiveRecord::TestCase
+ self.use_transactional_fixtures = false unless supports_savepoints?
+
+ def setup
+ super
+ organization = Organization.create
+ @member = Member.create
+ MemberDetail.create(organization: organization, member: @member)
+ end
+
+ def test_should_not_has_one_through_model
+ class << @member.organization
+ def save(*args)
+ super
+ raise 'Oh noes!'
+ end
+ end
+ assert_nothing_raised { @member.save }
+ end
+end
+
class TestAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase
self.use_transactional_fixtures = false unless supports_savepoints?
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index fc91b728c2..ac570ecbe0 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -262,7 +262,7 @@ class FinderTest < ActiveRecord::TestCase
end
def test_take_bang_missing
- assert_raises ActiveRecord::RecordNotFound do
+ assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
Topic.where("title = 'This title does not exist'").take!
end
end
@@ -282,7 +282,7 @@ class FinderTest < ActiveRecord::TestCase
end
def test_first_bang_missing
- assert_raises ActiveRecord::RecordNotFound do
+ assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
Topic.where("title = 'This title does not exist'").first!
end
end
@@ -296,7 +296,7 @@ class FinderTest < ActiveRecord::TestCase
def test_model_class_responds_to_first_bang
assert Topic.first!
Topic.delete_all
- assert_raises ActiveRecord::RecordNotFound do
+ assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
Topic.first!
end
end
@@ -318,7 +318,7 @@ class FinderTest < ActiveRecord::TestCase
def test_model_class_responds_to_second_bang
assert Topic.second!
Topic.delete_all
- assert_raises ActiveRecord::RecordNotFound do
+ assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
Topic.second!
end
end
@@ -340,7 +340,7 @@ class FinderTest < ActiveRecord::TestCase
def test_model_class_responds_to_third_bang
assert Topic.third!
Topic.delete_all
- assert_raises ActiveRecord::RecordNotFound do
+ assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
Topic.third!
end
end
@@ -362,7 +362,7 @@ class FinderTest < ActiveRecord::TestCase
def test_model_class_responds_to_fourth_bang
assert Topic.fourth!
Topic.delete_all
- assert_raises ActiveRecord::RecordNotFound do
+ assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
Topic.fourth!
end
end
@@ -384,7 +384,7 @@ class FinderTest < ActiveRecord::TestCase
def test_model_class_responds_to_fifth_bang
assert Topic.fifth!
Topic.delete_all
- assert_raises ActiveRecord::RecordNotFound do
+ assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
Topic.fifth!
end
end
@@ -396,14 +396,14 @@ class FinderTest < ActiveRecord::TestCase
end
def test_last_bang_missing
- assert_raises ActiveRecord::RecordNotFound do
+ assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
Topic.where("title = 'This title does not exist'").last!
end
end
def test_model_class_responds_to_last_bang
assert_equal topics(:fifth), Topic.last!
- assert_raises ActiveRecord::RecordNotFound do
+ assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
Topic.delete_all
Topic.last!
end
@@ -802,7 +802,9 @@ class FinderTest < ActiveRecord::TestCase
def test_find_by_one_attribute_bang
assert_equal topics(:first), Topic.find_by_title!("The First Topic")
- assert_raise(ActiveRecord::RecordNotFound) { Topic.find_by_title!("The First Topic!") }
+ assert_raises_with_message(ActiveRecord::RecordNotFound, "Couldn't find Topic") do
+ Topic.find_by_title!("The First Topic!")
+ end
end
def test_find_by_on_attribute_that_is_a_reserved_word
@@ -1071,6 +1073,11 @@ class FinderTest < ActiveRecord::TestCase
assert_equal nil, Post.find_by("1 = 0")
end
+ test "find_by with associations" do
+ assert_equal authors(:david), Post.find_by(author: authors(:david)).author
+ assert_equal authors(:mary) , Post.find_by(author: authors(:mary) ).author
+ end
+
test "find_by doesn't have implicit ordering" do
assert_sql(/^((?!ORDER).)*$/) { Post.find_by(id: posts(:eager_other).id) }
end
@@ -1113,4 +1120,10 @@ class FinderTest < ActiveRecord::TestCase
end
end)
end
+
+ def assert_raises_with_message(exception_class, message, &block)
+ err = assert_raises(exception_class) { block.call }
+ assert_match message, err.message
+ end
+
end
diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb
index d7ee56374d..93fb502410 100644
--- a/activerecord/test/cases/schema_dumper_test.rb
+++ b/activerecord/test/cases/schema_dumper_test.rb
@@ -371,6 +371,11 @@ class SchemaDumperTest < ActiveRecord::TestCase
output = standard_dump
assert_match(/^\s+add_foreign_key "fk_test_has_fk"[^\n]+\n\s+add_foreign_key "lessons_students"/, output)
end
+
+ def test_do_not_dump_foreign_keys_for_ignored_tables
+ output = dump_table_schema "authors"
+ assert_equal ["authors"], output.scan(/^\s*add_foreign_key "([^"]+)".+$/).flatten
+ end
end
class CreateDogMigration < ActiveRecord::Migration
diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb
index 9cfe041de2..5cccf2dda5 100644
--- a/activerecord/test/cases/transactions_test.rb
+++ b/activerecord/test/cases/transactions_test.rb
@@ -492,6 +492,37 @@ class TransactionTest < ActiveRecord::TestCase
assert topic.frozen?, 'not frozen'
end
+ # The behavior of killed threads having a status of "aborting" was changed
+ # in Ruby 2.0, so Thread#kill on 1.9 will prematurely commit the transaction
+ # and there's nothing we can do about it.
+ unless RUBY_VERSION.start_with? '1.9'
+ def test_rollback_when_thread_killed
+ queue = Queue.new
+ thread = Thread.new do
+ Topic.transaction do
+ @first.approved = true
+ @second.approved = false
+ @first.save
+
+ queue.push nil
+ sleep
+
+ @second.save
+ end
+ end
+
+ queue.pop
+ thread.kill
+ thread.join
+
+ assert @first.approved?, "First should still be changed in the objects"
+ assert !@second.approved?, "Second should still be changed in the objects"
+
+ assert !Topic.find(1).approved?, "First shouldn't have been approved"
+ assert Topic.find(2).approved?, "Second should still be approved"
+ end
+ end
+
def test_restore_active_record_state_for_all_records_in_a_transaction
topic_without_callbacks = Class.new(ActiveRecord::Base) do
self.table_name = 'topics'
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index 0e5a28e3fc..4204057737 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,5 +1,36 @@
+* Delegation now works with ruby reserved words passed to `:to` option.
+
+ Fixes #16956.
+
+ *Agis Anastasopoulos*
+
+* Added method `#eql?` to `ActiveSupport::Duration`, in addition to `#==`.
+
+ Currently, the following returns `false`, contrary to expectation:
+
+ 1.minute.eql?(1.minute)
+
+ Adding method `#eql?` will make this behave like expected. Method `#eql?` is
+ just a bit stricter than `#==`, as it checks whether the argument is also a duration. Their
+ parts may be different though.
+
+ 1.minute.eql?(60.seconds) # => true
+ 1.minute.eql?(60) # => false
+
+ *Joost Lubach*
+
+* Time#change can now change nanoseconds (:nsec) as a higher-precision
+ alternative to microseconds (:usec).
+
+ *Agis Anastasooulos*
+
+* `MessageVerifier.new` raises an appropriate exception if the secret is `nil`.
+ This prevents `MessageVerifier#generate` from raising a cryptic error later on.
+
+ *Kostiantyn Kahanskyi*
+
* Introduced new configuration option `active_support.test_order` for
- specifying the order test cases are executed. This option currently defaults
+ specifying the order in which test cases are executed. This option currently defaults
to `:sorted` but will be changed to `:random` in Rails 5.0.
*Akira Matsuda*, *Godfrey Chan*
@@ -22,7 +53,7 @@
*Peter Jaros*
* `determine_constant_from_test_name` does no longer shadow `NameError`s
- which happen during constant autoloading.
+ which happens during constant autoloading.
Fixes #9933.
@@ -100,8 +131,8 @@
* Always instrument `ActiveSupport::Cache`.
- Since `ActiveSupport::Notifications` only instrument items when there
- are subscriber we don't need to disable instrumentation.
+ Since `ActiveSupport::Notifications` only instruments items when there
+ are attached subscribers, we don't need to disable instrumentation.
*Peter Wagenet*
@@ -124,17 +155,17 @@
`ActiveSupport::TimeWithZone#-` should return the same result as if we were
using `Time#-`:
- Time.now.end_of_day - Time.now.beginning_of_day #=> 86399.999999999
+ Time.now.end_of_day - Time.now.beginning_of_day # => 86399.999999999
Before:
- Time.zone.now.end_of_day.nsec #=> 999999999
- Time.zone.now.end_of_day - Time.zone.now.beginning_of_day #=> 86400.0
+ Time.zone.now.end_of_day.nsec # => 999999999
+ Time.zone.now.end_of_day - Time.zone.now.beginning_of_day # => 86400.0
After:
Time.zone.now.end_of_day - Time.zone.now.beginning_of_day
- #=> 86399.999999999
+ # => 86399.999999999
*Gordon Chan*
@@ -143,12 +174,12 @@
Before:
ActiveSupport::NumberHelper.number_to_rounded Rational(1000, 3), precision: 2
- #=> "330.00"
+ # => "330.00"
After:
ActiveSupport::NumberHelper.number_to_rounded Rational(1000, 3), precision: 2
- #=> "333.33"
+ # => "333.33"
See #15379.
@@ -230,9 +261,9 @@
*Xavier Noria*
-* Fixed backward compatibility isues introduced in 326e652.
+* Fixed backward compatibility issues introduced in 326e652.
- Empty Hash or Array should not present in serialization result.
+ Empty Hash or Array should not be present in serialization result.
{a: []}.to_query # => ""
{a: {}}.to_query # => ""
@@ -251,20 +282,20 @@
This fixes the current situation of:
- 1.second.eql?(1.second) #=> false
+ 1.second.eql?(1.second) # => false
`eql?` also requires that the other object is an `ActiveSupport::Duration`.
This requirement makes `ActiveSupport::Duration`'s behavior consistent with
the behavior of Ruby's numeric types:
- 1.eql?(1.0) #=> false
- 1.0.eql?(1) #=> false
+ 1.eql?(1.0) # => false
+ 1.0.eql?(1) # => false
- 1.second.eql?(1) #=> false (was true)
- 1.eql?(1.second) #=> false
+ 1.second.eql?(1) # => false (was true)
+ 1.eql?(1.second) # => false
{ 1 => "foo", 1.0 => "bar" }
- #=> { 1 => "foo", 1.0 => "bar" }
+ # => { 1 => "foo", 1.0 => "bar" }
{ 1 => "foo", 1.second => "bar" }
# now => { 1 => "foo", 1.second => "bar" }
@@ -272,11 +303,11 @@
And though the behavior of these hasn't changed, for reference:
- 1 == 1.0 #=> true
- 1.0 == 1 #=> true
+ 1 == 1.0 # => true
+ 1.0 == 1 # => true
- 1 == 1.second #=> true
- 1.second == 1 #=> true
+ 1 == 1.second # => true
+ 1.second == 1 # => true
*Emily Dobervich*
@@ -286,8 +317,8 @@
*Pavel Pravosud*
-* `HashWithIndifferentAccess` better respects `#to_hash` on objects it's
- given. In particular, `.new`, `#update`, `#merge`, `#replace` all accept
+* `HashWithIndifferentAccess` better respects `#to_hash` on objects it
+ recieves. In particular, `.new`, `#update`, `#merge`, `#replace` all accept
objects which respond to `#to_hash`, even if those objects are not Hashes
directly.
diff --git a/activesupport/lib/active_support/concern.rb b/activesupport/lib/active_support/concern.rb
index 9d5cee54e3..342d3a9d52 100644
--- a/activesupport/lib/active_support/concern.rb
+++ b/activesupport/lib/active_support/concern.rb
@@ -95,7 +95,7 @@ module ActiveSupport
# end
#
# class Host
- # include Bar # works, Bar takes care now of its dependencies
+ # include Bar # It works, now Bar takes care of its dependencies
# end
module Concern
class MultipleIncludedBlocks < StandardError #:nodoc:
diff --git a/activesupport/lib/active_support/core_ext/module/delegation.rb b/activesupport/lib/active_support/core_ext/module/delegation.rb
index e926392952..570585b89a 100644
--- a/activesupport/lib/active_support/core_ext/module/delegation.rb
+++ b/activesupport/lib/active_support/core_ext/module/delegation.rb
@@ -1,8 +1,16 @@
+require 'set'
+
class Module
# Error generated by +delegate+ when a method is called on +nil+ and +allow_nil+
# option is not used.
class DelegationError < NoMethodError; end
+ RUBY_RESERVED_WORDS = Set.new(
+ %w(alias and BEGIN begin break case class def defined? do else elsif END
+ end ensure false for if in module next nil not or redo rescue retry
+ return self super then true undef unless until when while yield)
+ ).freeze
+
# Provides a +delegate+ class method to easily expose contained objects'
# public methods as your own.
#
@@ -163,7 +171,7 @@ class Module
line = line.to_i
to = to.to_s
- to = 'self.class' if to == 'class'
+ to = "self.#{to}" if RUBY_RESERVED_WORDS.include?(to)
methods.each do |method|
# Attribute writer methods only accept one argument. Makes sure []=
diff --git a/activesupport/lib/active_support/core_ext/string/filters.rb b/activesupport/lib/active_support/core_ext/string/filters.rb
index 1dfaf76673..2b1583d4ac 100644
--- a/activesupport/lib/active_support/core_ext/string/filters.rb
+++ b/activesupport/lib/active_support/core_ext/string/filters.rb
@@ -3,7 +3,7 @@ class String
# the string, and then changing remaining consecutive whitespace
# groups into one space each.
#
- # Note that it handles both ASCII and Unicode whitespace like mongolian vowel separator (U+180E).
+ # Note that it handles both ASCII and Unicode whitespace.
#
# %{ Multi-line
# string }.squish # => "Multi-line string"
diff --git a/activesupport/lib/active_support/core_ext/string/inflections.rb b/activesupport/lib/active_support/core_ext/string/inflections.rb
index a943752f17..38d567c014 100644
--- a/activesupport/lib/active_support/core_ext/string/inflections.rb
+++ b/activesupport/lib/active_support/core_ext/string/inflections.rb
@@ -199,6 +199,7 @@ class String
# 'employee_salary'.humanize # => "Employee salary"
# 'author_id'.humanize # => "Author"
# 'author_id'.humanize(capitalize: false) # => "author"
+ # '_id'.humanize # => "Id"
def humanize(options = {})
ActiveSupport::Inflector.humanize(self, options)
end
diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb
index 6a7bf7445a..ab8307429a 100644
--- a/activesupport/lib/active_support/core_ext/time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/time/calculations.rb
@@ -64,11 +64,12 @@ class Time
# Returns a new Time where one or more of the elements have been changed according
# to the +options+ parameter. The time options (<tt>:hour</tt>, <tt>:min</tt>,
- # <tt>:sec</tt>, <tt>:usec</tt>) reset cascadingly, so if only the hour is passed,
- # then minute, sec, and usec is set to 0. If the hour and minute is passed, then
- # sec and usec is set to 0. The +options+ parameter takes a hash with any of these
- # keys: <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>, <tt>:hour</tt>, <tt>:min</tt>,
- # <tt>:sec</tt>, <tt>:usec</tt>.
+ # <tt>:sec</tt>, <tt>:usec</tt>, <tt>:nsec</tt>) reset cascadingly, so if only
+ # the hour is passed, then minute, sec, usec and nsec is set to 0. If the hour
+ # and minute is passed, then sec, usec and nsec is set to 0. The +options+
+ # parameter takes a hash with any of these keys: <tt>:year</tt>, <tt>:month</tt>,
+ # <tt>:day</tt>, <tt>:hour</tt>, <tt>:min</tt>, <tt>:sec</tt>, <tt>:usec</tt>
+ # <tt>:nsec</tt>. Path either <tt>:usec</tt> or <tt>:nsec</tt>, not both.
#
# Time.new(2012, 8, 29, 22, 35, 0).change(day: 1) # => Time.new(2012, 8, 1, 22, 35, 0)
# Time.new(2012, 8, 29, 22, 35, 0).change(year: 1981, day: 1) # => Time.new(1981, 8, 1, 22, 35, 0)
@@ -80,7 +81,13 @@ class Time
new_hour = options.fetch(:hour, hour)
new_min = options.fetch(:min, options[:hour] ? 0 : min)
new_sec = options.fetch(:sec, (options[:hour] || options[:min]) ? 0 : sec)
- new_usec = options.fetch(:usec, (options[:hour] || options[:min] || options[:sec]) ? 0 : Rational(nsec, 1000))
+
+ if new_nsec = options[:nsec]
+ raise ArgumentError, "Can't change both :nsec and :usec at the same time: #{options.inspect}" if options[:usec]
+ new_usec = Rational(new_nsec, 1000)
+ else
+ new_usec = options.fetch(:usec, (options[:hour] || options[:min] || options[:sec]) ? 0 : Rational(nsec, 1000))
+ end
if utc?
::Time.utc(new_year, new_month, new_day, new_hour, new_min, new_sec, new_usec)
diff --git a/activesupport/lib/active_support/duration.rb b/activesupport/lib/active_support/duration.rb
index 1a6c02a39b..e861a17426 100644
--- a/activesupport/lib/active_support/duration.rb
+++ b/activesupport/lib/active_support/duration.rb
@@ -7,7 +7,7 @@ module ActiveSupport
# Time#advance, respectively. It mainly supports the methods on Numeric.
#
# 1.month.ago # equivalent to Time.now.advance(months: -1)
- class Duration < ProxyObject
+ class Duration
attr_accessor :value, :parts
def initialize(value, parts) #:nodoc:
@@ -53,8 +53,14 @@ module ActiveSupport
end
end
+ def to_s
+ @value.to_s
+ end
+
+ # Returns +true+ if +other+ is also a Duration instance, which has the
+ # same parts as this one.
def eql?(other)
- other.is_a?(Duration) && self == other
+ Duration === other && other.value.eql?(value)
end
def self.===(other) #:nodoc:
@@ -89,6 +95,10 @@ module ActiveSupport
to_i
end
+ def respond_to_missing?(method, include_private=false) #:nodoc
+ @value.respond_to?(method, include_private)
+ end
+
protected
def sum(sign, time = ::Time.current) #:nodoc:
diff --git a/activesupport/lib/active_support/message_verifier.rb b/activesupport/lib/active_support/message_verifier.rb
index 8e6e1dcfeb..6cb2884fb7 100644
--- a/activesupport/lib/active_support/message_verifier.rb
+++ b/activesupport/lib/active_support/message_verifier.rb
@@ -27,6 +27,7 @@ module ActiveSupport
class InvalidSignature < StandardError; end
def initialize(secret, options = {})
+ raise ArgumentError, 'Secret should not be nil.' unless secret
@secret = secret
@digest = options[:digest] || 'SHA1'
@serializer = options[:serializer] || Marshal
diff --git a/activesupport/lib/active_support/multibyte/unicode.rb b/activesupport/lib/active_support/multibyte/unicode.rb
index 89009d1f55..7d45961515 100644
--- a/activesupport/lib/active_support/multibyte/unicode.rb
+++ b/activesupport/lib/active_support/multibyte/unicode.rb
@@ -42,7 +42,6 @@ module ActiveSupport
0x0085, # White_Space # Cc <control-0085>
0x00A0, # White_Space # Zs NO-BREAK SPACE
0x1680, # White_Space # Zs OGHAM SPACE MARK
- 0x180E, # White_Space # Zs MONGOLIAN VOWEL SEPARATOR
(0x2000..0x200A).to_a, # White_Space # Zs [11] EN QUAD..HAIR SPACE
0x2028, # White_Space # Zl LINE SEPARATOR
0x2029, # White_Space # Zp PARAGRAPH SEPARATOR
diff --git a/activesupport/test/core_ext/duration_test.rb b/activesupport/test/core_ext/duration_test.rb
index a89202f518..f4d504914e 100644
--- a/activesupport/test/core_ext/duration_test.rb
+++ b/activesupport/test/core_ext/duration_test.rb
@@ -40,14 +40,22 @@ class DurationTest < ActiveSupport::TestCase
assert !(1.day == 'foo')
end
+ def test_to_s
+ assert_equal "1", 1.second.to_s
+ end
+
def test_eql
rubinius_skip "Rubinius' #eql? definition relies on #instance_of? " \
"which behaves oddly for the sake of backward-compatibility."
assert 1.minute.eql?(1.minute)
+ assert 1.minute.eql?(60.seconds)
assert 2.days.eql?(48.hours)
assert !1.second.eql?(1)
assert !1.eql?(1.second)
+ assert 1.minute.eql?(180.seconds - 2.minutes)
+ assert !1.minute.eql?(60)
+ assert !1.minute.eql?('foo')
end
def test_inspect
@@ -90,8 +98,8 @@ class DurationTest < ActiveSupport::TestCase
def test_since_and_ago
t = Time.local(2000)
- assert t + 1, 1.second.since(t)
- assert t - 1, 1.second.ago(t)
+ assert_equal t + 1, 1.second.since(t)
+ assert_equal t - 1, 1.second.ago(t)
end
def test_since_and_ago_without_argument
@@ -183,4 +191,9 @@ class DurationTest < ActiveSupport::TestCase
cased = case 1.day when 1.day then "ok" end
assert_equal cased, "ok"
end
+
+ def test_respond_to
+ assert_respond_to 1.day, :since
+ assert_respond_to 1.day, :zero?
+ end
end
diff --git a/activesupport/test/core_ext/module_test.rb b/activesupport/test/core_ext/module_test.rb
index 380f5ad42b..3c49c4d14f 100644
--- a/activesupport/test/core_ext/module_test.rb
+++ b/activesupport/test/core_ext/module_test.rb
@@ -56,8 +56,14 @@ Developer = Struct.new(:client) do
delegate :name, :to => :client, :prefix => nil
end
+Event = Struct.new(:case) do
+ delegate :foo, :to => :case
+end
+
Tester = Struct.new(:client) do
delegate :name, :to => :client, :prefix => false
+
+ def foo; 1; end
end
Product = Struct.new(:name) do
@@ -495,4 +501,9 @@ class MethodAliasingTest < ActiveSupport::TestCase
assert_equal 'duck_with_orange', @instance.duck
assert FooClassWithBarMethod.public_method_defined?(:duck)
end
+
+ def test_delegate_with_case
+ event = Event.new(Tester.new)
+ assert_equal 1, event.foo
+ end
end
diff --git a/activesupport/test/core_ext/object/duplicable_test.rb b/activesupport/test/core_ext/object/duplicable_test.rb
index 34679cb362..8cc39ae7b9 100644
--- a/activesupport/test/core_ext/object/duplicable_test.rb
+++ b/activesupport/test/core_ext/object/duplicable_test.rb
@@ -4,7 +4,7 @@ require 'active_support/core_ext/object/duplicable'
require 'active_support/core_ext/numeric/time'
class DuplicableTest < ActiveSupport::TestCase
- RAISE_DUP = [nil, false, true, :symbol, 1, 2.3, 5.seconds, method(:puts)]
+ RAISE_DUP = [nil, false, true, :symbol, 1, 2.3, method(:puts)]
ALLOW_DUP = ['1', Object.new, /foo/, [], {}, Time.now, Class.new, Module.new]
# Needed to support Ruby 1.9.x, as it doesn't allow dup on BigDecimal, instead
diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb
index 18b4727c3b..2f4691817f 100644
--- a/activesupport/test/core_ext/string_ext_test.rb
+++ b/activesupport/test/core_ext/string_ext_test.rb
@@ -189,10 +189,10 @@ class StringInflectionsTest < ActiveSupport::TestCase
end
def test_string_squish
- original = %{\u180E\u180E A string surrounded by unicode mongolian vowel separators,
- with tabs(\t\t), newlines(\n\n), unicode nextlines(\u0085\u0085) and many spaces( ). \u180E\u180E}
+ original = %{\u205f\u3000 A string surrounded by various unicode spaces,
+ with tabs(\t\t), newlines(\n\n), unicode nextlines(\u0085\u0085) and many spaces( ). \u00a0\u2007}
- expected = "A string surrounded by unicode mongolian vowel separators, " +
+ expected = "A string surrounded by various unicode spaces, " +
"with tabs( ), newlines( ), unicode nextlines( ) and many spaces( )."
# Make sure squish returns what we expect:
diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb
index 9a5bd19be2..d59775001b 100644
--- a/activesupport/test/core_ext/time_ext_test.rb
+++ b/activesupport/test/core_ext/time_ext_test.rb
@@ -387,6 +387,8 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
assert_equal Time.local(2005,1,2,11, 6, 0, 0), Time.local(2005,1,2,11,22,33,44).change(:min => 6)
assert_equal Time.local(2005,1,2,11,22, 7, 0), Time.local(2005,1,2,11,22,33,44).change(:sec => 7)
assert_equal Time.local(2005,1,2,11,22,33, 8), Time.local(2005,1,2,11,22,33,44).change(:usec => 8)
+ assert_equal Time.local(2005,1,2,11,22,33, 8), Time.local(2005,1,2,11,22,33,2).change(:nsec => 8000)
+ assert_raise(ArgumentError) { Time.local(2005,1,2,11,22,33, 8).change(:usec => 1, :nsec => 1) }
end
def test_utc_change
@@ -396,6 +398,7 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
assert_equal Time.utc(2005,2,22,16), Time.utc(2005,2,22,15,15,10).change(:hour => 16)
assert_equal Time.utc(2005,2,22,16,45), Time.utc(2005,2,22,15,15,10).change(:hour => 16, :min => 45)
assert_equal Time.utc(2005,2,22,15,45), Time.utc(2005,2,22,15,15,10).change(:min => 45)
+ assert_equal Time.utc(2005,1,2,11,22,33,8), Time.utc(2005,1,2,11,22,33,2).change(:nsec => 8000)
end
def test_offset_change
@@ -405,7 +408,11 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
assert_equal Time.new(2005,2,22,16,0,0,"-08:00"), Time.new(2005,2,22,15,15,10,"-08:00").change(:hour => 16)
assert_equal Time.new(2005,2,22,16,45,0,"-08:00"), Time.new(2005,2,22,15,15,10,"-08:00").change(:hour => 16, :min => 45)
assert_equal Time.new(2005,2,22,15,45,0,"-08:00"), Time.new(2005,2,22,15,15,10,"-08:00").change(:min => 45)
+ assert_equal Time.new(2005,2,22,15,15,10,"-08:00"), Time.new(2005,2,22,15,15,0,"-08:00").change(:sec => 10)
+ assert_equal 10, Time.new(2005,2,22,15,15,0,"-08:00").change(:usec => 10).usec
+ assert_equal 10, Time.new(2005,2,22,15,15,0,"-08:00").change(:nsec => 10).nsec
assert_raise(ArgumentError) { Time.new(2005, 2, 22, 15, 15, 45, "-08:00").change(:usec => 1000000) }
+ assert_raise(ArgumentError) { Time.new(2005, 2, 22, 15, 15, 45, "-08:00").change(:nsec => 1000000000) }
end
def test_advance
diff --git a/activesupport/test/message_verifier_test.rb b/activesupport/test/message_verifier_test.rb
index a5748d28ba..28035bc428 100644
--- a/activesupport/test/message_verifier_test.rb
+++ b/activesupport/test/message_verifier_test.rb
@@ -69,6 +69,13 @@ class MessageVerifierTest < ActiveSupport::TestCase
"undefined class/module MessageVerifierTest::AutoloadClass"], exception.message
end
+ def test_raise_error_when_secret_is_nil
+ exception = assert_raise(ArgumentError) do
+ ActiveSupport::MessageVerifier.new(nil)
+ end
+ assert_equal exception.message, 'Secret should not be nil.'
+ end
+
def assert_not_verified(message)
assert_raise(ActiveSupport::MessageVerifier::InvalidSignature) do
@verifier.verify(message)
diff --git a/guides/source/3_0_release_notes.md b/guides/source/3_0_release_notes.md
index 8122d6c235..46be2613ab 100644
--- a/guides/source/3_0_release_notes.md
+++ b/guides/source/3_0_release_notes.md
@@ -545,7 +545,7 @@ These are the main changes in Active Support:
* `String#to_time` and `String#to_datetime` handle fractional seconds.
* Added support to new callbacks for around filter object that respond to `:before` and `:after` used in before and after callbacks.
* The `ActiveSupport::OrderedHash#to_a` method returns an ordered set of arrays. Matches Ruby 1.9's `Hash#to_a`.
-* `MissingSourceFile` exists as a constant but it is now just equals to `LoadError`.
+* `MissingSourceFile` exists as a constant but it is now just equal to `LoadError`.
* Added `Class#class_attribute`, to be able to declare a class-level attribute whose value is inheritable and overwritable by subclasses.
* Finally removed `DeprecatedCallbacks` in `ActiveRecord::Associations`.
* `Object#metaclass` is now `Kernel#singleton_class` to match Ruby.
diff --git a/guides/source/4_2_release_notes.md b/guides/source/4_2_release_notes.md
index cb461feff8..c59affea8f 100644
--- a/guides/source/4_2_release_notes.md
+++ b/guides/source/4_2_release_notes.md
@@ -25,9 +25,8 @@ If you're upgrading an existing application, it's a great idea to have good test
coverage before going in. You should also first upgrade to Rails 4.1 in case you
haven't and make sure your application still runs as expected before attempting
to upgrade to Rails 4.2. A list of things to watch out for when upgrading is
-available in the
-[Upgrading Ruby on Rails](upgrading_ruby_on_rails.html#upgrading-from-rails-4-1-to-rails-4-2)
-guide.
+available in the guide: [Upgrading Ruby on
+Rails](upgrading_ruby_on_rails.html#upgrading-from-rails-4-1-to-rails-4-2)
Major Features
@@ -36,9 +35,12 @@ Major Features
### Active Job, Action Mailer #deliver_later
Active Job is a new framework in Rails 4.2. It is an adapter layer on top of
-queuing systems like [Resque](https://github.com/resque/resque), [Delayed Job](https://github.com/collectiveidea/delayed_job), [Sidekiq](https://github.com/mperham/sidekiq), and more. You can write your
-jobs with the Active Job API, and it'll run on all these queues with no changes
-(it comes pre-configured with an inline runner).
+queuing systems like [Resque](https://github.com/resque/resque), [Delayed
+Job](https://github.com/collectiveidea/delayed_job),
+[Sidekiq](https://github.com/mperham/sidekiq), and more.
+
+You can write your jobs with the Active Job API, and it'll run on all these
+queues with no changes (it comes pre-configured with an inline runner).
Building on top of Active Job, Action Mailer now comes with a `#deliver_later`
method, which adds your email to be sent as a job to a queue, so it doesn't
@@ -52,11 +54,45 @@ deserialize it at run time.
### Adequate Record
-Rails 4.2 comes with a performance improvement feature called Adequate Record
-for Active Record. A lot of common queries are now up to twice as fast in Rails
-4.2!
+Adequate Record is a set of refactorings that make Active Record `find` and
+`find_by` methods and some association queries upto 2x faster.
+
+It works by caching SQL query patterns while executing the Active Record calls.
+The cache helps skip parts of the computation involved in the transformation of
+the calls into SQL queries. More details in [Aaron Patterson's
+post](http://tenderlovemaking.com/2014/02/19/adequaterecord-pro-like-activerecord.html).
+
+Nothing special has to be done to activate this feature. Most `find` and
+`find_by` calls and association queries will use it automatically. Examples:
+
+```ruby
+Post.find 1 # caches query pattern
+Post.find 2 # uses the cached pattern
+
+Post.find_by_title 'first post' # caches query pattern
+Post.find_by_title 'second post' # uses the cached pattern
+
+post.comments # caches query pattern
+post.comments(true) # uses cached pattern
+```
+
+The caching is not used in the following scenarios:
+
+- The model has a default scope
+- The model uses single table inheritence to inherit from another model
+- `find` with a list of ids. eg:
+
+ ```ruby
+ Post.find(1,2,3)
+ OR
+ Post.find [1,2]
+ ```
+
+- `find_by` with sql fragments:
-TODO: add some technical details
+ ```ruby
+ Post.find_by "published_at < ?", 2.weeks.ago
+ ```
### Web Console
@@ -148,6 +184,21 @@ class UsersController < ApplicationController
end
```
+### Default host for `rails server`
+
+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 both http://127.0.0.1:3000
+and http://localhost:3000 would continue to work as before on your own machine.
+
+However, with this change you would no longer be able to access the Rails server
+from a different machine (e.g. your development environment is in a virtual
+machine and you would like to access it from the host machine), you would need
+to start the server with `rails server -b 0.0.0.0` to restore the old behavior.
+
+If you do this, be sure to configure your firewall properly such that only
+trusted machines on your network can access your development server.
+
### Production logging
The default log level in the `production` environment is now `:debug`. This
@@ -293,7 +344,7 @@ Please refer to the [Changelog][railties] for detailed changes.
namespace: my_app_development
# config/production.rb
- MyApp::Application.configure do
+ Rails.application.configure do
config.middleware.use ExceptionNotifier, config_for(:exception_notification)
end
```
diff --git a/guides/source/active_job_basics.md b/guides/source/active_job_basics.md
index 7b3081993d..2fd242cb2c 100644
--- a/guides/source/active_job_basics.md
+++ b/guides/source/active_job_basics.md
@@ -99,46 +99,18 @@ If no adapter is set, the job is immediately executed.
### Backends
-Active Job has adapters for the following queueing backends:
-
-* [Backburner](https://github.com/nesquena/backburner)
-* [Delayed Job](https://github.com/collectiveidea/delayed_job)
-* [Qu](https://github.com/bkeepers/qu)
-* [Que](https://github.com/chanks/que)
-* [QueueClassic 2.x](https://github.com/ryandotsmith/queue_classic/tree/v2.2.3)
-* [Resque 1.x](https://github.com/resque/resque/tree/1-x-stable)
-* [Sidekiq](https://github.com/mperham/sidekiq)
-* [Sneakers](https://github.com/jondot/sneakers)
-* [Sucker Punch](https://github.com/brandonhilkert/sucker_punch)
-
-#### Backends Features
-
-| | Async | Queues | Delayed | Priorities | Timeout | Retries |
-|-----------------------|-------|--------|-----------|------------|---------|---------|
-| **Backburner** | Yes | Yes | Yes | Yes | Job | Global |
-| **Delayed Job** | Yes | Yes | Yes | Job | Global | Global |
-| **Que** | Yes | Yes | Yes | Job | No | Job |
-| **Queue Classic** | Yes | Yes | No* | No | No | No |
-| **Resque** | Yes | Yes | Yes (Gem) | Queue | Global | Yes |
-| **Sidekiq** | Yes | Yes | Yes | Queue | No | Job |
-| **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 |
-
-NOTE:
-* Queue Classic does not support Job scheduling. However you can implement this
-yourself or you can use the queue_classic-later gem. See the documentation for
-ActiveJob::QueueAdapters::QueueClassicAdapter.
-
-### Change Backends
-
-You can easily change your adapter:
+Active Job has built-in adapters for multiple queueing backends (Sidekiq,
+Resque, Delayed Job and others). To get an up-to-date list of the adapters
+see the API Documentation for [ActiveJob::QueueAdapters](http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters.html).
+
+### Changing the Backend
+
+You can easily change your queueing backend:
```ruby
# be sure to have the adapter gem in your Gemfile and follow the adapter specific
# installation and deployment instructions
-YourApp::Application.config.active_job.queue_adapter = :sidekiq
+Rails.application.config.active_job.queue_adapter = :sidekiq
```
diff --git a/guides/source/active_record_querying.md b/guides/source/active_record_querying.md
index cb243c95f5..e1a465c64f 100644
--- a/guides/source/active_record_querying.md
+++ b/guides/source/active_record_querying.md
@@ -340,16 +340,14 @@ The `find_in_batches` method is similar to `find_each`, since both retrieve batc
```ruby
# Give add_invoices an array of 1000 invoices at a time
-Invoice.find_in_batches(include: :invoice_lines) do |invoices|
+Invoice.find_in_batches do |invoices|
export.add_invoices(invoices)
end
```
-NOTE: The `:include` option allows you to name associations that should be loaded alongside with the models.
-
##### Options for `find_in_batches`
-The `find_in_batches` method accepts the same `:batch_size` and `:start` options as `find_each`, as well as most of the options allowed by the regular `find` method, except for `:order` and `:limit`, which are reserved for internal use by `find_in_batches`.
+The `find_in_batches` method accepts the same `:batch_size` and `:start` options as `find_each`.
Conditions
----------
diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md
index 2e1a3ee5dc..de42f13145 100644
--- a/guides/source/active_support_core_extensions.md
+++ b/guides/source/active_support_core_extensions.md
@@ -1268,7 +1268,7 @@ The method `squish` strips leading and trailing whitespace, and substitutes runs
There's also the destructive version `String#squish!`.
-Note that it handles both ASCII and Unicode whitespace like mongolian vowel separator (U+180E).
+Note that it handles both ASCII and Unicode whitespace.
NOTE: Defined in `active_support/core_ext/string/filters.rb`.
diff --git a/guides/source/command_line.md b/guides/source/command_line.md
index a074b849c6..b9014724bd 100644
--- a/guides/source/command_line.md
+++ b/guides/source/command_line.md
@@ -130,7 +130,7 @@ Example:
`rails generate controller CreditCards open debit credit close`
Credit card controller with URLs like /credit_cards/debit.
- Controller: app/controllers/credit_card_controller.rb
+ Controller: app/controllers/credit_cards_controller.rb
Test: test/controllers/credit_cards_controller_test.rb
Views: app/views/credit_cards/debit.html.erb [...]
Helper: app/helpers/credit_cards_helper.rb
diff --git a/guides/source/configuring.md b/guides/source/configuring.md
index 667281d1aa..220946e8d5 100644
--- a/guides/source/configuring.md
+++ b/guides/source/configuring.md
@@ -108,7 +108,7 @@ numbers. New applications filter out passwords by adding the following `config.f
* `config.log_formatter` defines the formatter of the Rails logger. This option defaults to an instance of `ActiveSupport::Logger::SimpleFormatter` for all modes except production, where it defaults to `Logger::Formatter`.
-* `config.log_level` defines the verbosity of the Rails logger. This option defaults to `:debug` for all modes except production, where it defaults to `:info`.
+* `config.log_level` defines the verbosity of the Rails logger. This option defaults to `:debug` for all environments.
* `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.
diff --git a/guides/source/debugging_rails_applications.md b/guides/source/debugging_rails_applications.md
index 88c6210296..1a647f8375 100644
--- a/guides/source/debugging_rails_applications.md
+++ b/guides/source/debugging_rails_applications.md
@@ -138,7 +138,7 @@ Rails.logger.level = 0 # at any time
This is useful when you want to log under development or staging, but you don't want to flood your production log with unnecessary information.
-TIP: The default Rails log level is `info` in production mode and `debug` in development and test mode.
+TIP: The default Rails log level is `debug` in all environments.
### Sending Messages
diff --git a/guides/source/maintenance_policy.md b/guides/source/maintenance_policy.md
index 6f8584b3b7..050a64ddf3 100644
--- a/guides/source/maintenance_policy.md
+++ b/guides/source/maintenance_policy.md
@@ -39,7 +39,10 @@ Only the latest release series will receive bug fixes. When enough bugs are
fixed and its deemed worthy to release a new gem, this is the branch it happens
from.
-**Currently included series:** `4.1.Z`, `4.0.Z`.
+In special situations, where someone from the Core Team agrees to support more series,
+they are included in the list of supported series.
+
+**Currently included series:** `4.2.Z`, `4.1.Z` (Supported by Rafael França).
Security Issues
---------------
@@ -54,7 +57,7 @@ be built from 1.2.2, and then added to the end of 1-2-stable. This means that
security releases are easy to upgrade to if you're running the latest version
of Rails.
-**Currently included series:** `4.1.Z`, `4.0.Z`.
+**Currently included series:** `4.2.Z`, `4.1.Z`.
Severe Security Issues
----------------------
@@ -63,7 +66,7 @@ For severe security issues we will provide new versions as above, and also the
last major release series will receive patches and new versions. The
classification of the security issue is judged by the core team.
-**Currently included series:** `4.1.Z`, `4.0.Z`, `3.2.Z`.
+**Currently included series:** `4.2.Z`, `4.1.Z`, `3.2.Z`.
Unsupported Release Series
--------------------------
diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md
index 2c6797d1fd..4e538693f7 100644
--- a/guides/source/upgrading_ruby_on_rails.md
+++ b/guides/source/upgrading_ruby_on_rails.md
@@ -55,15 +55,51 @@ a [pull request](https://github.com/rails/rails/edit/master/guides/source/upgrad
### Web Console
-First, add `gem 'web-console', '~> 2.0.0.beta3'` to the `:development` group in your Gemfile and run `bundle install` (it won't have been included when you upgraded Rails). Once it's been installed, you can simply drop a reference to the console helper (i.e., `<%= console %>`) into any view you want to enable it for. A console will also be provided on any error page you view in your development environment.
+First, add `gem 'web-console', '~> 2.0'` to the `:development` group in your Gemfile and run `bundle install` (it won't have been included when you upgraded Rails). Once it's been installed, you can simply drop a reference to the console helper (i.e., `<%= console %>`) into any view you want to enable it for. A console will also be provided on any error page you view in your development environment.
-Additionally, you can tell Rails to automatically mount a VT100-compatible console on a predetermined path by setting `config.web_console.automount = true` in your `config/environments/development.rb`. You can specify the path by setting `config.web_console.default_mount_path` (note that this defaults to `/console`).
+Additionally, you can tell Rails to automatically mount a VT100-compatible console on a predetermined path by setting the appropriate configuration flags in your development config:
-TODO: Update `web-console` version to release version.
+```ruby
+# config/environments/development.rb
+
+config.web_console.automount = true
+config.web_console.default_mount_path = '/terminal' # Optional, defaults to /console
+```
### Responders
-TODO: mention https://github.com/rails/rails/pull/16526
+`respond_with` and the class-level `respond_to` methods have been extracted to the `responders` gem. To use them, simply add `gem 'responders', '~> 2.0'` to your Gemfile. Calls to `respond_with` and `respond_to` (again, at the class level) will no longer work without having included the `responders` gem in your dependencies:
+
+```ruby
+# app/controllers/users_controller.rb
+
+class UsersController < ApplicationController
+ respond_to :html, :json
+
+ def show
+ @user = User.find(params[:id])
+ respond_with @user
+ end
+end
+```
+
+Instance-level `respond_to` is unaffected and does not require the additional gem:
+
+```ruby
+# app/controllers/users_controller.rb
+
+class UsersController < ApplicationController
+ def show
+ @user = User.find(params[:id])
+ respond_to do |format|
+ format.html
+ format.json { render json: @user }
+ end
+ end
+end
+```
+
+See [#16526](https://github.com/rails/rails/pull/16526) for more details.
### Error handling in transaction callbacks
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index 7d6521b2a8..2e7f134101 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,3 +1,20 @@
+* Specify dummy app's db migrate path in plugin's test_helper.rb.
+
+ Fixes #16877.
+
+ *Yukio Mizuta*
+
+* Inject `Rack::Lock` if `config.eager_load` is false.
+
+ Fixes #15089.
+
+ *Xavier Noria*
+
+* Change the path of dummy app location in plugin's test_helper.rb for cases
+ you specify dummy_path option.
+
+ *Yukio Mizuta*
+
* Fix a bug in the `gem` method for Rails templates when non-String options
are used.
@@ -56,7 +73,7 @@
namespace: my_app_development
# config/production.rb
- MyApp::Application.configure do
+ Rails.application.configure do
config.middleware.use ExceptionNotifier, config_for(:exception_notification)
end
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index 67d5bac700..bc966e87c6 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -217,7 +217,7 @@ module Rails
# namespace: my_app_development
#
# # config/production.rb
- # MyApp::Application.configure do
+ # Rails.application.configure do
# config.middleware.use ExceptionNotifier, config_for(:exception_notification)
# end
def config_for(name)
diff --git a/railties/lib/rails/application/default_middleware_stack.rb b/railties/lib/rails/application/default_middleware_stack.rb
index a00afe008c..d1789192ef 100644
--- a/railties/lib/rails/application/default_middleware_stack.rb
+++ b/railties/lib/rails/application/default_middleware_stack.rb
@@ -66,7 +66,11 @@ module Rails
end
def allow_concurrency?
- config.allow_concurrency.nil? ? config.cache_classes : config.allow_concurrency
+ if config.allow_concurrency.nil?
+ config.cache_classes && config.eager_load
+ else
+ config.allow_concurrency
+ end
end
def load_rack_cache
diff --git a/railties/lib/rails/commands/server.rb b/railties/lib/rails/commands/server.rb
index c479e92ae0..e39f0920af 100644
--- a/railties/lib/rails/commands/server.rb
+++ b/railties/lib/rails/commands/server.rb
@@ -24,7 +24,7 @@ module Rails
opts.on("-p", "--port=port", Integer,
"Runs Rails on the specified port.", "Default: 3000") { |v| options[:Port] = v }
opts.on("-b", "--binding=IP", String,
- "Binds Rails to the specified IP.", "Default: 0.0.0.0") { |v| options[:Host] = v }
+ "Binds Rails to the specified IP.", "Default: localhost") { |v| options[:Host] = v }
opts.on("-c", "--config=file", String,
"Uses a custom rackup configuration.") { |v| options[:config] = v }
opts.on("-d", "--daemon", "Runs server as a Daemon.") { options[:daemonize] = true }
@@ -126,10 +126,6 @@ module Rails
puts "=> Rails #{Rails.version} application starting in #{Rails.env} on #{url}"
puts "=> Run `rails server -h` for more startup options"
- if options[:Host].to_s.match(/0\.0\.0\.0/)
- puts "=> Notice: server is listening on all interfaces (#{options[:Host]}). Consider using 127.0.0.1 (--binding option)"
- end
-
puts "=> Ctrl-C to shutdown server" unless options[:daemonize]
end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
index 2cd8040f35..92ff0de030 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
@@ -16,7 +16,8 @@ Rails.application.configure do
# Enable Rack::Cache to put a simple HTTP cache in front of your application
# Add `rack-cache` to your Gemfile before enabling this.
- # For large-scale production use, consider using a caching reverse proxy like NGINX, varnish or squid.
+ # For large-scale production use, consider using a caching reverse proxy like
+ # NGINX, varnish or squid.
# config.action_dispatch.rack_cache = true
# Disable Rails's static asset server (Apache or NGINX will already do this).
@@ -38,7 +39,7 @@ Rails.application.configure do
<%- end -%>
# Specifies the header that your server uses for sending files.
- # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for Apache
+ # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
# config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
@@ -57,7 +58,7 @@ Rails.application.configure do
# config.cache_store = :mem_cache_store
# Enable serving of images, stylesheets, and JavaScripts from an asset server.
- # config.action_controller.asset_host = "http://assets.example.com"
+ # config.action_controller.asset_host = 'http://assets.example.com'
# Ignore bad email addresses and do not raise email delivery errors.
# Set this to true and configure the email server for immediate delivery to raise delivery errors.
diff --git a/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb b/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb
index 1e26a313cd..c6e2247e16 100644
--- a/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb
+++ b/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb
@@ -1,7 +1,10 @@
# Configure Rails Environment
ENV["RAILS_ENV"] = "test"
-require File.expand_path("../dummy/config/environment.rb", __FILE__)
+require File.expand_path("../../<%= options[:dummy_path] -%>/config/environment.rb", __FILE__)
+<% unless options[:skip_active_record] -%>
+ActiveRecord::Migrator.migrations_paths = [File.expand_path("../../<%= options[:dummy_path] -%>/db/migrate", __FILE__)]
+<% end -%>
require "rails/test_help"
Rails.backtrace_cleaner.remove_silencers!
diff --git a/railties/lib/rails/generators/test_unit/job/job_generator.rb b/railties/lib/rails/generators/test_unit/job/job_generator.rb
index 05d9fde280..466b74bd12 100644
--- a/railties/lib/rails/generators/test_unit/job/job_generator.rb
+++ b/railties/lib/rails/generators/test_unit/job/job_generator.rb
@@ -6,7 +6,7 @@ module TestUnit # :nodoc:
check_class_collision suffix: 'JobTest'
def create_test_file
- template 'unit_test.rb.erb', File.join('test/job', class_path, "#{file_name}_test.rb")
+ template 'unit_test.rb.erb', File.join('test/jobs', class_path, "#{file_name}_test.rb")
end
end
end
diff --git a/railties/lib/rails/test_unit/testing.rake b/railties/lib/rails/test_unit/testing.rake
index 285e2ce846..957deb8a60 100644
--- a/railties/lib/rails/test_unit/testing.rake
+++ b/railties/lib/rails/test_unit/testing.rake
@@ -3,7 +3,7 @@ require 'rails/test_unit/sub_test_task'
task default: :test
-desc 'Runs test:units, test:functionals, test:generators, test:integration together'
+desc 'Runs test:units, test:functionals, test:generators, test:integration, test:jobs together'
task :test do
Rails::TestTask.test_creator(Rake.application.top_level_tasks).invoke_rake_task
end
@@ -13,7 +13,7 @@ namespace :test do
# Placeholder task for other Railtie and plugins to enhance. See Active Record for an example.
end
- task :run => ['test:units', 'test:functionals', 'test:generators', 'test:integration']
+ task :run => ['test:units', 'test:functionals', 'test:generators', 'test:integration', 'test:jobs']
# Inspired by: http://ngauthier.com/2012/02/quick-tests-with-bash.html
desc "Run tests quickly by merging all types and not resetting db"
@@ -28,7 +28,7 @@ namespace :test do
Rails::TestTask.new(single: "test:prepare")
- ["models", "helpers", "controllers", "mailers", "integration"].each do |name|
+ ["models", "helpers", "controllers", "mailers", "integration", "jobs"].each do |name|
Rails::TestTask.new(name => "test:prepare") do |t|
t.pattern = "test/#{name}/**/*_test.rb"
end
diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb
index f8b4ee30d8..0eddf644d9 100644
--- a/railties/test/application/configuration_test.rb
+++ b/railties/test/application/configuration_test.rb
@@ -182,7 +182,7 @@ module ApplicationTests
test "application is always added to eager_load namespaces" do
require "#{app_path}/config/application"
- assert Rails.application, Rails.application.config.eager_load_namespaces
+ assert_includes Rails.application.config.eager_load_namespaces, AppTemplate::Application
end
test "the application can be eager loaded even when there are no frameworks" do
diff --git a/railties/test/application/middleware_test.rb b/railties/test/application/middleware_test.rb
index a905598d80..caef39d16f 100644
--- a/railties/test/application/middleware_test.rb
+++ b/railties/test/application/middleware_test.rb
@@ -94,13 +94,20 @@ module ApplicationTests
assert !middleware.include?("ActiveRecord::Migration::CheckPending")
end
- test "removes lock if cache classes is set" do
+ test "includes lock if cache_classes is set but eager_load is not" do
add_to_config "config.cache_classes = true"
boot!
+ assert middleware.include?("Rack::Lock")
+ end
+
+ test "does not include lock if cache_classes is set and so is eager_load" do
+ add_to_config "config.cache_classes = true"
+ add_to_config "config.eager_load = true"
+ boot!
assert !middleware.include?("Rack::Lock")
end
- test "removes lock if allow concurrency is set" do
+ test "does not include lock if allow_concurrency is set" do
add_to_config "config.allow_concurrency = true"
boot!
assert !middleware.include?("Rack::Lock")
diff --git a/railties/test/application/test_runner_test.rb b/railties/test/application/test_runner_test.rb
index 118f22995e..032b11a95f 100644
--- a/railties/test/application/test_runner_test.rb
+++ b/railties/test/application/test_runner_test.rb
@@ -109,6 +109,17 @@ module ApplicationTests
end
end
+ def test_run_jobs
+ create_test_file :jobs, 'foo_job'
+ create_test_file :jobs, 'bar_job'
+ create_test_file :models, 'foo'
+ run_test_jobs_command.tap do |output|
+ assert_match "FooJobTest", output
+ assert_match "BarJobTest", output
+ assert_match "2 runs, 2 assertions, 0 failures", output
+ end
+ end
+
def test_run_functionals
create_test_file :mailers, 'foo_mailer'
create_test_file :controllers, 'bar_controller'
@@ -132,11 +143,11 @@ module ApplicationTests
end
def test_run_all_suites
- suites = [:models, :helpers, :unit, :controllers, :mailers, :functional, :integration]
+ suites = [:models, :helpers, :unit, :controllers, :mailers, :functional, :integration, :jobs]
suites.each { |suite| create_test_file suite, "foo_#{suite}" }
run_test_command('') .tap do |output|
suites.each { |suite| assert_match "Foo#{suite.to_s.camelize}Test", output }
- assert_match "7 runs, 7 assertions, 0 failures", output
+ assert_match "8 runs, 8 assertions, 0 failures", output
end
end
@@ -245,7 +256,7 @@ module ApplicationTests
def run_test_command(arguments = 'test/unit/test_test.rb')
run_task ['test', arguments]
end
- %w{ mailers models helpers units controllers functionals integration }.each do |type|
+ %w{ mailers models helpers units controllers functionals integration jobs }.each do |type|
define_method("run_test_#{type}_command") do
run_task ["test:#{type}"]
end
diff --git a/railties/test/generators/plugin_generator_test.rb b/railties/test/generators/plugin_generator_test.rb
index 985644e8af..ed4e100a9b 100644
--- a/railties/test/generators/plugin_generator_test.rb
+++ b/railties/test/generators/plugin_generator_test.rb
@@ -54,7 +54,10 @@ class PluginGeneratorTest < Rails::Generators::TestCase
run_generator
assert_file "README.rdoc", /Bukkits/
assert_no_file "config/routes.rb"
- assert_file "test/test_helper.rb"
+ assert_file "test/test_helper.rb" do |content|
+ assert_match(/require.+test\/dummy\/config\/environment/, content)
+ assert_match(/ActiveRecord::Migrator\.migrations_paths.+test\/dummy\/db\/migrate/, content)
+ end
assert_file "test/bukkits_test.rb", /assert_kind_of Module, Bukkits/
end
@@ -270,6 +273,10 @@ class PluginGeneratorTest < Rails::Generators::TestCase
assert_file "spec/dummy"
assert_file "spec/dummy/config/application.rb"
assert_no_file "test/dummy"
+ assert_file "test/test_helper.rb" do |content|
+ assert_match(/require.+spec\/dummy\/config\/environment/, content)
+ assert_match(/ActiveRecord::Migrator\.migrations_paths.+spec\/dummy\/db\/migrate/, content)
+ end
end
def test_creating_dummy_application_with_different_name
@@ -277,6 +284,10 @@ class PluginGeneratorTest < Rails::Generators::TestCase
assert_file "spec/fake"
assert_file "spec/fake/config/application.rb"
assert_no_file "test/dummy"
+ assert_file "test/test_helper.rb" do |content|
+ assert_match(/require.+spec\/fake\/config\/environment/, content)
+ assert_match(/ActiveRecord::Migrator\.migrations_paths.+spec\/fake\/db\/migrate/, content)
+ end
end
def test_creating_dummy_without_tests_but_with_dummy_path
@@ -284,6 +295,7 @@ class PluginGeneratorTest < Rails::Generators::TestCase
assert_file "spec/dummy"
assert_file "spec/dummy/config/application.rb"
assert_no_file "test"
+ assert_no_file "test/test_helper.rb"
assert_file '.gitignore' do |contents|
assert_match(/spec\/dummy/, contents)
end