aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTING.md2
-rw-r--r--actionmailer/CHANGELOG.md5
-rw-r--r--actionpack/lib/action_controller/test_case.rb6
-rw-r--r--actionpack/lib/action_dispatch/http/filter_parameters.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/cookies.rb3
-rw-r--r--actionpack/lib/action_dispatch/middleware/reloader.rb4
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb6
-rw-r--r--actionpack/lib/action_dispatch/testing/integration.rb6
-rw-r--r--actionview/lib/action_view/helpers/asset_tag_helper.rb7
-rw-r--r--actionview/lib/action_view/helpers/form_helper.rb62
-rw-r--r--actionview/lib/action_view/helpers/form_options_helper.rb2
-rw-r--r--actionview/test/template/asset_tag_helper_test.rb8
-rw-r--r--actionview/test/template/url_helper_test.rb10
-rw-r--r--activejob/CHANGELOG.md6
-rw-r--r--activejob/Rakefile4
-rw-r--r--activejob/lib/active_job/arguments.rb2
-rw-r--r--activejob/lib/active_job/core.rb2
-rw-r--r--activejob/lib/active_job/enqueuing.rb1
-rw-r--r--activejob/lib/active_job/queue_adapters/inline_adapter.rb2
-rw-r--r--activejob/lib/active_job/queue_adapters/qu_adapter.rb2
-rw-r--r--activejob/lib/active_job/queue_adapters/que_adapter.rb4
-rw-r--r--activejob/lib/active_job/queue_adapters/sneakers_adapter.rb2
-rw-r--r--activejob/lib/active_job/queue_adapters/sucker_punch_adapter.rb2
-rw-r--r--activejob/lib/active_job/test_helper.rb4
-rw-r--r--activejob/lib/rails/generators/job/job_generator.rb2
-rw-r--r--activejob/test/cases/test_helper_test.rb22
-rw-r--r--activejob/test/integration/queuing_test.rb2
-rw-r--r--activejob/test/support/integration/adapters/que.rb2
-rw-r--r--activejob/test/support/que/inline.rb6
-rw-r--r--activerecord/CHANGELOG.md24
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/quoting.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb11
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb10
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb10
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb10
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb72
-rw-r--r--activerecord/lib/active_record/connection_adapters/column.rb7
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb3
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb26
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb2
-rw-r--r--activerecord/lib/active_record/errors.rb4
-rw-r--r--activerecord/lib/active_record/persistence.rb4
-rw-r--r--activerecord/lib/active_record/schema_dumper.rb14
-rw-r--r--activerecord/lib/active_record/secure_token.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql/active_schema_test.rb37
-rw-r--r--activerecord/test/cases/adapters/mysql/case_sensitivity_test.rb1
-rw-r--r--activerecord/test/cases/adapters/mysql/table_options_test.rb42
-rw-r--r--activerecord/test/cases/adapters/mysql2/active_schema_test.rb37
-rw-r--r--activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb1
-rw-r--r--activerecord/test/cases/adapters/mysql2/table_options_test.rb42
-rw-r--r--activerecord/test/cases/adapters/postgresql/collation_test.rb53
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb3
-rw-r--r--activerecord/test/cases/callbacks_test.rb2
-rw-r--r--activerecord/test/cases/schema_dumper_test.rb20
-rw-r--r--activerecord/test/schema/postgresql_specific_schema.rb11
-rw-r--r--activerecord/test/schema/schema.rb14
-rw-r--r--guides/source/4_2_release_notes.md5
-rw-r--r--guides/source/active_record_validations.md2
-rw-r--r--guides/source/caching_with_rails.md22
-rw-r--r--guides/source/debugging_rails_applications.md85
-rw-r--r--guides/source/upgrading_ruby_on_rails.md14
-rw-r--r--railties/CHANGELOG.md2
-rw-r--r--railties/lib/rails/engine.rb4
-rw-r--r--railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb5
-rw-r--r--railties/lib/rails/templates/rails/mailers/index.html.erb4
-rw-r--r--railties/lib/rails/templates/rails/mailers/mailer.html.erb2
-rw-r--r--railties/lib/rails/test_help.rb6
-rw-r--r--railties/lib/rails/test_unit/sub_test_task.rb126
-rw-r--r--railties/test/application/configuration/base_test.rb37
-rw-r--r--railties/test/application/configuration/custom_test.rb73
-rw-r--r--railties/test/application/mailer_previews_test.rb30
-rw-r--r--railties/test/application/rake_test.rb4
-rw-r--r--railties/test/generators_test.rb2
-rw-r--r--railties/test/test_info_test.rb60
78 files changed, 676 insertions, 473 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 421e7088cb..699b6fd2d1 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -8,7 +8,7 @@ Ruby on Rails is a volunteer effort. We encourage you to pitch in. [Join the tea
*We only accept bug reports and pull requests on GitHub*.
-* If you have a question about how to use Ruby on Rails, please [ask the rubyonrails-talk mailing list](https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-talk).
+* If you have a question about how to use Ruby on Rails, please [ask it on the rubyonrails-talk mailing list](https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-talk).
* If you have a change or new feature in mind, please [suggest it on the rubyonrails-core mailing list](https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core) and start writing code.
diff --git a/actionmailer/CHANGELOG.md b/actionmailer/CHANGELOG.md
index 0d47ce855a..f88f2c0c86 100644
--- a/actionmailer/CHANGELOG.md
+++ b/actionmailer/CHANGELOG.md
@@ -1,3 +1,8 @@
+* Mailer preview now uses `url_for` to fix links to emails for apps running on
+ a subdirectory.
+
+ *Remo Mueller*
+
* Mailer previews no longer crash when the `mail` method wasn't called
(`NullMail`).
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index 33c24999f9..ca7ba90c40 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -604,7 +604,7 @@ module ActionController
def process(action, *args)
check_required_ivars
- if kwarg_request?(*args)
+ if kwarg_request?(args)
parameters, session, body, flash, http_method, format, xhr = args[0].values_at(:params, :session, :body, :flash, :method, :format, :xhr)
else
http_method, parameters, session, flash = args
@@ -745,7 +745,7 @@ module ActionController
private
def process_with_kwargs(http_method, action, *args)
- if kwarg_request?(*args)
+ if kwarg_request?(args)
args.first.merge!(method: http_method)
process(action, *args)
else
@@ -757,7 +757,7 @@ module ActionController
end
REQUEST_KWARGS = %i(params session flash method body xhr)
- def kwarg_request?(*args)
+ def kwarg_request?(args)
args[0].respond_to?(:keys) && (
(args[0].key?(:format) && args[0].keys.size == 1) ||
args[0].keys.any? { |k| REQUEST_KWARGS.include?(k) }
diff --git a/actionpack/lib/action_dispatch/http/filter_parameters.rb b/actionpack/lib/action_dispatch/http/filter_parameters.rb
index 2b851cc28d..3170389b36 100644
--- a/actionpack/lib/action_dispatch/http/filter_parameters.rb
+++ b/actionpack/lib/action_dispatch/http/filter_parameters.rb
@@ -16,7 +16,7 @@ module ActionDispatch
# env["action_dispatch.parameter_filter"] = [:foo, "bar"]
# => replaces the value to all keys matching /foo|bar/i with "[FILTERED]"
#
- # env["action_dispatch.parameter_filter"] = lambda do |k,v|
+ # env["action_dispatch.parameter_filter"] = -> (k, v) do
# v.reverse! if k =~ /secret/i
# end
# => reverses the value to all keys matching /secret/i
diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb
index bb6a73afb5..dd1f140051 100644
--- a/actionpack/lib/action_dispatch/middleware/cookies.rb
+++ b/actionpack/lib/action_dispatch/middleware/cookies.rb
@@ -79,6 +79,9 @@ module ActionDispatch
# domain: %w(.example.com .example.org) # Allow the cookie
# # for concrete domain names.
#
+ # * <tt>:tld_length</tt> - When using <tt>:domain => :all</tt>, this option can be used to explicitly
+ # set the TLD length when using a short (<= 3 character) domain that is being interpreted as part of a TLD.
+ # For example, to share cookies between user1.lvh.me and user2.lvh.me, set <tt>:tld_length</tt> to 1.
# * <tt>:expires</tt> - The time at which this cookie expires, as a \Time object.
# * <tt>:secure</tt> - Whether this cookie is only transmitted to HTTPS servers.
# Default is +false+.
diff --git a/actionpack/lib/action_dispatch/middleware/reloader.rb b/actionpack/lib/action_dispatch/middleware/reloader.rb
index 15b5a48535..6c7fba00cb 100644
--- a/actionpack/lib/action_dispatch/middleware/reloader.rb
+++ b/actionpack/lib/action_dispatch/middleware/reloader.rb
@@ -11,9 +11,9 @@ module ActionDispatch
# the response body. This is important for streaming responses such as the
# following:
#
- # self.response_body = lambda { |response, output|
+ # self.response_body = -> (response, output) do
# # code here which refers to application models
- # }
+ # end
#
# Cleanup callbacks will not be called until after the response_body lambda
# is evaluated, ensuring that it can refer to application models and other
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 49009a45cc..0a444ddffc 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -418,7 +418,7 @@ module ActionDispatch
# A pattern can also point to a +Rack+ endpoint i.e. anything that
# responds to +call+:
#
- # match 'photos/:id', to: lambda {|hash| [200, {}, ["Coming soon"]] }, via: :get
+ # match 'photos/:id', to: -> (hash) { [200, {}, ["Coming soon"]] }, via: :get
# match 'photos/:id', to: PhotoRackApp, via: :get
# # Yes, controller actions are just rack endpoints
# match 'photos/:id', to: PhotosController.action(:show), via: :get
@@ -470,7 +470,7 @@ module ActionDispatch
# +call+ or a string representing a controller's action.
#
# match 'path', to: 'controller#action', via: :get
- # match 'path', to: lambda { |env| [200, {}, ["Success!"]] }, via: :get
+ # match 'path', to: -> (env) { [200, {}, ["Success!"]] }, via: :get
# match 'path', to: RackApp, via: :get
#
# [:on]
@@ -899,7 +899,7 @@ module ActionDispatch
#
# Requests to routes can be constrained based on specific criteria:
#
- # constraints(lambda { |req| req.env["HTTP_USER_AGENT"] =~ /iPhone/ }) do
+ # constraints(-> (req) { req.env["HTTP_USER_AGENT"] =~ /iPhone/ }) do
# resources :iphones
# end
#
diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb
index 3800c61dab..b1bd6ae6d5 100644
--- a/actionpack/lib/action_dispatch/testing/integration.rb
+++ b/actionpack/lib/action_dispatch/testing/integration.rb
@@ -81,7 +81,7 @@ module ActionDispatch
#
# xhr :get, '/feed', params: { since: 201501011400 }
def xml_http_request(request_method, path, *args)
- if kwarg_request?(*args)
+ if kwarg_request?(args)
params, headers, env = args.first.values_at(:params, :headers, :env)
else
params = args[0]
@@ -291,7 +291,7 @@ module ActionDispatch
end
def process_with_kwargs(http_method, path, *args)
- if kwarg_request?(*args)
+ if kwarg_request?(args)
process(http_method, path, *args)
else
non_kwarg_request_warning if args.present?
@@ -300,7 +300,7 @@ module ActionDispatch
end
REQUEST_KWARGS = %i(params headers env xhr)
- def kwarg_request?(*args)
+ def kwarg_request?(args)
args[0].respond_to?(:keys) && args[0].keys.any? { |k| REQUEST_KWARGS.include?(k) }
end
diff --git a/actionview/lib/action_view/helpers/asset_tag_helper.rb b/actionview/lib/action_view/helpers/asset_tag_helper.rb
index 60fc9ee1a2..e32f8e219e 100644
--- a/actionview/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionview/lib/action_view/helpers/asset_tag_helper.rb
@@ -207,6 +207,7 @@ module ActionView
# # => <img alt="Icon" class="menu_icon" src="/icons/icon.gif" />
def image_tag(source, options={})
options = options.symbolize_keys
+ check_for_image_tag_errors(options)
src = options[:src] = path_to_image(source)
@@ -325,6 +326,12 @@ module ActionView
[size, size]
end
end
+
+ def check_for_image_tag_errors(options)
+ if options[:size] && (options[:height] || options[:width])
+ raise ArgumentError, "Cannot pass a :size option with a :height or :width option"
+ end
+ end
end
end
end
diff --git a/actionview/lib/action_view/helpers/form_helper.rb b/actionview/lib/action_view/helpers/form_helper.rb
index 7fdeca5ea8..06394df3d4 100644
--- a/actionview/lib/action_view/helpers/form_helper.rb
+++ b/actionview/lib/action_view/helpers/form_helper.rb
@@ -1631,7 +1631,7 @@ module ActionView
# target labels for radio_button tags (where the value is used in the ID of the input tag).
#
# ==== Examples
- # label(:post, :title)
+ # label(:title)
# # => <label for="post_title">Title</label>
#
# You can localize your labels based on model and attribute names.
@@ -1644,7 +1644,7 @@ module ActionView
#
# Which then will result in
#
- # label(:post, :body)
+ # label(:body)
# # => <label for="post_body">Write your entire text here</label>
#
# Localization can also be based purely on the translation of the attribute-name
@@ -1655,19 +1655,19 @@ module ActionView
# post:
# cost: "Total cost"
#
- # label(:post, :cost)
+ # label(:cost)
# # => <label for="post_cost">Total cost</label>
#
- # label(:post, :title, "A short title")
+ # label(:title, "A short title")
# # => <label for="post_title">A short title</label>
#
- # label(:post, :title, "A short title", class: "title_label")
+ # label(:title, "A short title", class: "title_label")
# # => <label for="post_title" class="title_label">A short title</label>
#
- # label(:post, :privacy, "Public Post", value: "public")
+ # label(:privacy, "Public Post", value: "public")
# # => <label for="post_privacy_public">Public Post</label>
#
- # label(:post, :terms) do
+ # label(:terms) do
# 'Accept <a href="/terms">Terms</a>.'.html_safe
# end
def label(method, text = nil, options = {}, &block)
@@ -1718,16 +1718,17 @@ module ActionView
# hashes instead of arrays.
#
# # Let's say that @post.validated? is 1:
- # check_box("post", "validated")
+ # check_box("validated")
# # => <input name="post[validated]" type="hidden" value="0" />
# # <input checked="checked" type="checkbox" id="post_validated" name="post[validated]" value="1" />
#
# # Let's say that @puppy.gooddog is "no":
- # check_box("puppy", "gooddog", {}, "yes", "no")
+ # check_box("gooddog", {}, "yes", "no")
# # => <input name="puppy[gooddog]" type="hidden" value="no" />
# # <input type="checkbox" id="puppy_gooddog" name="puppy[gooddog]" value="yes" />
#
- # check_box("eula", "accepted", { class: 'eula_check' }, "yes", "no")
+ # # Let's say that @eula.accepted is "no":
+ # check_box("accepted", { class: 'eula_check' }, "yes", "no")
# # => <input name="eula[accepted]" type="hidden" value="no" />
# # <input type="checkbox" class="eula_check" id="eula_accepted" name="eula[accepted]" value="yes" />
def check_box(method, options = {}, checked_value = "1", unchecked_value = "0")
@@ -1742,13 +1743,14 @@ module ActionView
# +options+ hash. You may pass HTML options there as well.
#
# # Let's say that @post.category returns "rails":
- # radio_button("post", "category", "rails")
- # radio_button("post", "category", "java")
+ # radio_button("category", "rails")
+ # radio_button("category", "java")
# # => <input type="radio" id="post_category_rails" name="post[category]" value="rails" checked="checked" />
# # <input type="radio" id="post_category_java" name="post[category]" value="java" />
#
- # radio_button("user", "receive_newsletter", "yes")
- # radio_button("user", "receive_newsletter", "no")
+ # # Let's say that @user.category returns "no":
+ # radio_button("receive_newsletter", "yes")
+ # radio_button("receive_newsletter", "no")
# # => <input type="radio" id="user_receive_newsletter_yes" name="user[receive_newsletter]" value="yes" />
# # <input type="radio" id="user_receive_newsletter_no" name="user[receive_newsletter]" value="no" checked="checked" />
def radio_button(method, tag_value, options = {})
@@ -1761,14 +1763,17 @@ module ActionView
# shown.
#
# ==== Examples
- # hidden_field(:signup, :pass_confirm)
- # # => <input type="hidden" id="signup_pass_confirm" name="signup[pass_confirm]" value="#{@signup.pass_confirm}" />
+ # # Let's say that @signup.pass_confirm returns true:
+ # hidden_field(:pass_confirm)
+ # # => <input type="hidden" id="signup_pass_confirm" name="signup[pass_confirm]" value="true" />
#
- # hidden_field(:post, :tag_list)
- # # => <input type="hidden" id="post_tag_list" name="post[tag_list]" value="#{@post.tag_list}" />
+ # # Let's say that @post.tag_list returns "blog, ruby":
+ # hidden_field(:tag_list)
+ # # => <input type="hidden" id="post_tag_list" name="post[tag_list]" value="blog, ruby" />
#
- # hidden_field(:user, :token)
- # # => <input type="hidden" id="user_token" name="user[token]" value="#{@user.token}" />
+ # # Let's say that @user.token returns "abcde":
+ # hidden_field(:token)
+ # # => <input type="hidden" id="user_token" name="user[token]" value="abcde" />
#
def hidden_field(method, options = {})
@emitted_hidden_id = true if method == :id
@@ -1789,19 +1794,24 @@ module ActionView
# * <tt>:accept</tt> - If set to one or multiple mime-types, the user will be suggested a filter when choosing a file. You still need to set up model validations.
#
# ==== Examples
- # file_field(:user, :avatar)
+ # # Let's say that @user has avatar:
+ # file_field(:avatar)
# # => <input type="file" id="user_avatar" name="user[avatar]" />
#
- # file_field(:post, :image, :multiple => true)
- # # => <input type="file" id="post_image" name="post[image]" multiple="true" />
+ # # Let's say that @post has image:
+ # file_field(:image, :multiple => true)
+ # # => <input type="file" id="post_image" name="post[image][]" multiple="multiple" />
#
- # file_field(:post, :attached, accept: 'text/html')
+ # # Let's say that @post has attached:
+ # file_field(:attached, accept: 'text/html')
# # => <input accept="text/html" type="file" id="post_attached" name="post[attached]" />
#
- # file_field(:post, :image, accept: 'image/png,image/gif,image/jpeg')
+ # # Let's say that @post has image:
+ # file_field(:image, accept: 'image/png,image/gif,image/jpeg')
# # => <input type="file" id="post_image" name="post[image]" accept="image/png,image/gif,image/jpeg" />
#
- # file_field(:attachment, :file, class: 'file_input')
+ # # Let's say that @attachment has file:
+ # file_field(:file, class: 'file_input')
# # => <input type="file" id="attachment_file" name="attachment[file]" class="file_input" />
def file_field(method, options = {})
self.multipart = true
diff --git a/actionview/lib/action_view/helpers/form_options_helper.rb b/actionview/lib/action_view/helpers/form_options_helper.rb
index 8a5928477f..38fee3b314 100644
--- a/actionview/lib/action_view/helpers/form_options_helper.rb
+++ b/actionview/lib/action_view/helpers/form_options_helper.rb
@@ -80,7 +80,7 @@ module ActionView
#
# When used with the <tt>collection_select</tt> helper, <tt>:disabled</tt> can also be a Proc that identifies those options that should be disabled.
#
- # collection_select(:post, :category_id, Category.all, :id, :name, {disabled: lambda{|category| category.archived? }})
+ # collection_select(:post, :category_id, Category.all, :id, :name, {disabled: -> (category) { category.archived? }})
#
# If the categories "2008 stuff" and "Christmas" return true when the method <tt>archived?</tt> is called, this would return:
# <select name="post[category_id]" id="post_category_id">
diff --git a/actionview/test/template/asset_tag_helper_test.rb b/actionview/test/template/asset_tag_helper_test.rb
index 02dce71496..6e6ce20924 100644
--- a/actionview/test/template/asset_tag_helper_test.rb
+++ b/actionview/test/template/asset_tag_helper_test.rb
@@ -464,6 +464,14 @@ class AssetTagHelperTest < ActionView::TestCase
assert_equal({:size => '16x10'}, options)
end
+ def test_image_tag_raises_an_error_for_competing_size_arguments
+ exception = assert_raise(ArgumentError) do
+ image_tag("gold.png", :height => "100", :width => "200", :size => "45x70")
+ end
+
+ assert_equal("Cannot pass a :size option with a :height or :width option", exception.message)
+ end
+
def test_favicon_link_tag
FaviconLinkToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
end
diff --git a/actionview/test/template/url_helper_test.rb b/actionview/test/template/url_helper_test.rb
index ef4df0407a..9ba837d4e7 100644
--- a/actionview/test/template/url_helper_test.rb
+++ b/actionview/test/template/url_helper_test.rb
@@ -379,6 +379,11 @@ class UrlHelperTest < ActiveSupport::TestCase
assert_dom_equal %{<a href="/">Listing</a>}, link_to_if(true, "Listing", url_hash)
end
+ def test_link_to_if_with_block
+ assert_equal "Fallback", link_to_if(false, "Showing", url_hash) { "Fallback" }
+ assert_dom_equal %{<a href="/">Listing</a>}, link_to_if(true, "Listing", url_hash) { "Fallback" }
+ end
+
def request_for_url(url, opts = {})
env = Rack::MockRequest.env_for("http://www.example.com#{url}", opts)
ActionDispatch::Request.new(env)
@@ -479,6 +484,11 @@ class UrlHelperTest < ActiveSupport::TestCase
link_to_unless_current("Listing", "http://www.example.com/")
end
+ def test_link_to_unless_with_block
+ assert_equal %{<a href="/">Showing</a>}, link_to_unless(false, "Showing", url_hash) { "Fallback" }
+ assert_dom_equal "Fallback", link_to_unless(true, "Listing", url_hash) { "Fallback" }
+ end
+
def test_mail_to
assert_dom_equal %{<a href="mailto:david@loudthinking.com">david@loudthinking.com</a>}, mail_to("david@loudthinking.com")
assert_dom_equal %{<a href="mailto:david@loudthinking.com">David Heinemeier Hansson</a>}, mail_to("david@loudthinking.com", "David Heinemeier Hansson")
diff --git a/activejob/CHANGELOG.md b/activejob/CHANGELOG.md
index 85a437a1dd..1c55c1a4b8 100644
--- a/activejob/CHANGELOG.md
+++ b/activejob/CHANGELOG.md
@@ -1,3 +1,9 @@
+* `assert_enqueued_jobs` and `assert_performed_jobs` in block form use the
+ given number as expected value. This makes the error message much easier to
+ understand.
+
+ *y-yagi*
+
* A generated job now inherents from `app/jobs/application_job.rb` by default.
*Jeroen van Baarsen*
diff --git a/activejob/Rakefile b/activejob/Rakefile
index 6c7e6aae6e..0e36bb81b3 100644
--- a/activejob/Rakefile
+++ b/activejob/Rakefile
@@ -35,7 +35,7 @@ namespace :test do
t.libs << 'test'
t.test_files = FileList['test/cases/**/*_test.rb']
t.verbose = true
- t.warning = true
+ t.warning = false
t.ruby_opts = ["--dev"] if defined?(JRUBY_VERSION)
end
@@ -54,7 +54,7 @@ namespace :test do
t.libs << 'test'
t.test_files = FileList['test/integration/**/*_test.rb']
t.verbose = true
- t.warning = true
+ t.warning = false
t.ruby_opts = ["--dev"] if defined?(JRUBY_VERSION)
end
end
diff --git a/activejob/lib/active_job/arguments.rb b/activejob/lib/active_job/arguments.rb
index f9392cf278..cdb37879b6 100644
--- a/activejob/lib/active_job/arguments.rb
+++ b/activejob/lib/active_job/arguments.rb
@@ -5,6 +5,8 @@ module ActiveJob
#
# Wraps the original exception raised as +original_exception+.
class DeserializationError < StandardError
+ # The original exception that was raised during deserialization of job
+ # arguments.
attr_reader :original_exception
def initialize(e) #:nodoc:
diff --git a/activejob/lib/active_job/core.rb b/activejob/lib/active_job/core.rb
index ddd7d1361c..acdfcdc791 100644
--- a/activejob/lib/active_job/core.rb
+++ b/activejob/lib/active_job/core.rb
@@ -1,4 +1,6 @@
module ActiveJob
+ # Provides general behavior that will be included into every Active Job
+ # object that inherits from ActiveJob::Base.
module Core
extend ActiveSupport::Concern
diff --git a/activejob/lib/active_job/enqueuing.rb b/activejob/lib/active_job/enqueuing.rb
index 430c17e1bf..98d92385dd 100644
--- a/activejob/lib/active_job/enqueuing.rb
+++ b/activejob/lib/active_job/enqueuing.rb
@@ -1,6 +1,7 @@
require 'active_job/arguments'
module ActiveJob
+ # Provides behavior for enqueuing and retrying jobs.
module Enqueuing
extend ActiveSupport::Concern
diff --git a/activejob/lib/active_job/queue_adapters/inline_adapter.rb b/activejob/lib/active_job/queue_adapters/inline_adapter.rb
index 1d06324c18..8ad5f4de07 100644
--- a/activejob/lib/active_job/queue_adapters/inline_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/inline_adapter.rb
@@ -14,7 +14,7 @@ module ActiveJob
end
def enqueue_at(*) #:nodoc:
- raise NotImplementedError.new("Use a queueing backend to enqueue jobs in the future. Read more at http://guides.rubyonrails.org/active_job_basics.html")
+ raise NotImplementedError, "Use a queueing backend to enqueue jobs in the future. Read more at http://guides.rubyonrails.org/active_job_basics.html"
end
end
end
diff --git a/activejob/lib/active_job/queue_adapters/qu_adapter.rb b/activejob/lib/active_job/queue_adapters/qu_adapter.rb
index 94584ef9d8..36f4c14911 100644
--- a/activejob/lib/active_job/queue_adapters/qu_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/qu_adapter.rb
@@ -23,7 +23,7 @@ module ActiveJob
end
def enqueue_at(job, timestamp, *args) #:nodoc:
- raise NotImplementedError
+ raise NotImplementedError, "This queueing backend does not support scheduling jobs. To see what features are supported go to http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters.html"
end
class JobWrapper < Qu::Job #:nodoc:
diff --git a/activejob/lib/active_job/queue_adapters/que_adapter.rb b/activejob/lib/active_job/queue_adapters/que_adapter.rb
index 84cc2845b0..a1a41ccc32 100644
--- a/activejob/lib/active_job/queue_adapters/que_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/que_adapter.rb
@@ -16,11 +16,11 @@ module ActiveJob
# Rails.application.config.active_job.queue_adapter = :que
class QueAdapter
def enqueue(job) #:nodoc:
- JobWrapper.enqueue job.serialize, queue: job.queue_name
+ JobWrapper.enqueue job.serialize
end
def enqueue_at(job, timestamp) #:nodoc:
- JobWrapper.enqueue job.serialize, queue: job.queue_name, run_at: Time.at(timestamp)
+ JobWrapper.enqueue job.serialize, run_at: Time.at(timestamp)
end
class JobWrapper < Que::Job #:nodoc:
diff --git a/activejob/lib/active_job/queue_adapters/sneakers_adapter.rb b/activejob/lib/active_job/queue_adapters/sneakers_adapter.rb
index f5737487ca..f102c6567e 100644
--- a/activejob/lib/active_job/queue_adapters/sneakers_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/sneakers_adapter.rb
@@ -28,7 +28,7 @@ module ActiveJob
end
def enqueue_at(job, timestamp) #:nodoc:
- raise NotImplementedError
+ raise NotImplementedError, "This queueing backend does not support scheduling jobs. To see what features are supported go to http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters.html"
end
class JobWrapper #:nodoc:
diff --git a/activejob/lib/active_job/queue_adapters/sucker_punch_adapter.rb b/activejob/lib/active_job/queue_adapters/sucker_punch_adapter.rb
index 64c93e8198..c6c35f8ab4 100644
--- a/activejob/lib/active_job/queue_adapters/sucker_punch_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/sucker_punch_adapter.rb
@@ -23,7 +23,7 @@ module ActiveJob
end
def enqueue_at(job, timestamp) #:nodoc:
- raise NotImplementedError
+ raise NotImplementedError, "This queueing backend does not support scheduling jobs. To see what features are supported go to http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters.html"
end
class JobWrapper #:nodoc:
diff --git a/activejob/lib/active_job/test_helper.rb b/activejob/lib/active_job/test_helper.rb
index 4efb4b72d2..9b307e8dc8 100644
--- a/activejob/lib/active_job/test_helper.rb
+++ b/activejob/lib/active_job/test_helper.rb
@@ -68,7 +68,7 @@ module ActiveJob
original_count = enqueued_jobs_size(only: only)
yield
new_count = enqueued_jobs_size(only: only)
- assert_equal original_count + number, new_count, "#{number} jobs expected, but #{new_count - original_count} were enqueued"
+ assert_equal number, new_count - original_count, "#{number} jobs expected, but #{new_count - original_count} were enqueued"
else
actual_count = enqueued_jobs_size(only: only)
assert_equal number, actual_count, "#{number} jobs expected, but #{actual_count} were enqueued"
@@ -164,7 +164,7 @@ module ActiveJob
original_count = performed_jobs.size
perform_enqueued_jobs(only: only) { yield }
new_count = performed_jobs.size
- assert_equal original_count + number, new_count,
+ assert_equal number, new_count - original_count,
"#{number} jobs expected, but #{new_count - original_count} were performed"
else
performed_jobs_size = performed_jobs.size
diff --git a/activejob/lib/rails/generators/job/job_generator.rb b/activejob/lib/rails/generators/job/job_generator.rb
index 86e4c5266c..2115fb9f71 100644
--- a/activejob/lib/rails/generators/job/job_generator.rb
+++ b/activejob/lib/rails/generators/job/job_generator.rb
@@ -1,6 +1,6 @@
require 'rails/generators/named_base'
-module Rails
+module Rails # :nodoc:
module Generators # :nodoc:
class JobGenerator < Rails::Generators::NamedBase # :nodoc:
desc 'This generator creates an active job file at app/jobs'
diff --git a/activejob/test/cases/test_helper_test.rb b/activejob/test/cases/test_helper_test.rb
index 19a2820a6e..04c4c446e2 100644
--- a/activejob/test/cases/test_helper_test.rb
+++ b/activejob/test/cases/test_helper_test.rb
@@ -31,6 +31,17 @@ class EnqueuedJobsTest < ActiveJob::TestCase
end
end
+ def test_assert_enqueued_jobs_message
+ HelloJob.perform_later('sean')
+ e = assert_raises Minitest::Assertion do
+ assert_enqueued_jobs 2 do
+ HelloJob.perform_later('sean')
+ end
+ end
+ assert_match "Expected: 2", e.message
+ assert_match "Actual: 1", e.message
+ end
+
def test_assert_enqueued_jobs_with_no_block
assert_nothing_raised do
HelloJob.perform_later('rafael')
@@ -230,6 +241,17 @@ class PerformedJobsTest < ActiveJob::TestCase
end
end
+ def test_assert_performed_jobs_message
+ HelloJob.perform_later('sean')
+ e = assert_raises Minitest::Assertion do
+ assert_performed_jobs 2 do
+ HelloJob.perform_later('sean')
+ end
+ end
+ assert_match "Expected: 2", e.message
+ assert_match "Actual: 1", e.message
+ end
+
def test_assert_performed_jobs_with_no_block
assert_nothing_raised do
perform_enqueued_jobs do
diff --git a/activejob/test/integration/queuing_test.rb b/activejob/test/integration/queuing_test.rb
index 09f5c329cc..96794ffef3 100644
--- a/activejob/test/integration/queuing_test.rb
+++ b/activejob/test/integration/queuing_test.rb
@@ -11,7 +11,7 @@ class QueuingTest < ActiveSupport::TestCase
end
test 'should not run jobs queued on a non-listening queue' do
- skip if adapter_is?(:inline) || adapter_is?(:sucker_punch)
+ skip if adapter_is?(:inline) || adapter_is?(:sucker_punch) || adapter_is?(:que)
old_queue = TestJob.queue_name
begin
diff --git a/activejob/test/support/integration/adapters/que.rb b/activejob/test/support/integration/adapters/que.rb
index ff5235bdc8..0cd8952a28 100644
--- a/activejob/test/support/integration/adapters/que.rb
+++ b/activejob/test/support/integration/adapters/que.rb
@@ -23,7 +23,7 @@ module QueJobsManager
@thread = Thread.new do
loop do
- Que::Job.work("integration_tests")
+ Que::Job.work
sleep 0.5
end
end
diff --git a/activejob/test/support/que/inline.rb b/activejob/test/support/que/inline.rb
index 2e210acb6b..0232da1370 100644
--- a/activejob/test/support/que/inline.rb
+++ b/activejob/test/support/que/inline.rb
@@ -3,7 +3,11 @@ require 'que'
Que::Job.class_eval do
class << self; alias_method :original_enqueue, :enqueue; end
def self.enqueue(*args)
- args.pop if args.last.is_a?(Hash)
+ if args.last.is_a?(Hash)
+ options = args.pop
+ options.delete(:run_at)
+ args << options unless options.empty?
+ end
self.run(*args)
end
end
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index b6a943eca7..09045087d9 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,25 @@
+* Dump indexes in `create_table` instead of `add_index`.
+
+ If the adapter supports indexes in create table, generated SQL is
+ slightly more efficient.
+
+ *Ryuta Kamizono*
+
+* Correctly dump `:options` on `create_table` for MySQL.
+
+ *Ryuta Kamizono*
+
+* PostgreSQL: `:collation` support for string and text columns.
+
+ Example:
+
+ create_table :foos do |t|
+ t.string :string_en, collation: 'en_US.UTF-8'
+ t.text :text_ja, collation: 'ja_JP.UTF-8'
+ end
+
+ *Ryuta Kamizono*
+
* Make `unscope` aware of "less than" and "greater than" conditions.
*TAKAHASHI Kazuaki*
@@ -745,7 +767,7 @@
*Yves Senn*
-* Fix bug with 'ActiveRecord::Type::Numeric' that caused negative values to
+* Fix bug with `ActiveRecord::Type::Numeric` that caused negative values to
be marked as having changed when set to the same negative value.
Closes #18161.
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb
index c0a2111571..30b2fca2ca 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb
@@ -18,9 +18,9 @@ module ActiveRecord
end
# Returns the maximum allowed length for an index name. This
- # limit is enforced by rails and Is less than or equal to
+ # limit is enforced by \Rails and is less than or equal to
# <tt>index_name_length</tt>. The gap between
- # <tt>index_name_length</tt> is to allow internal rails
+ # <tt>index_name_length</tt> is to allow internal \Rails
# operations to use prefixes in temporary operations.
def allowed_index_name_length
index_name_length
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
index 91c7298983..2c7409b2dc 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
@@ -123,6 +123,8 @@ module ActiveRecord
'f'
end
+ # Quote date/time values for use in SQL input. Includes microseconds
+ # if the value is a Time responding to usec.
def quoted_date(value)
if value.acts_like?(:time)
zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb
index f754df93b6..18d943f452 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb
@@ -14,10 +14,6 @@ module ActiveRecord
send m, o
end
- def visit_AddColumn(o)
- "ADD #{accept(o)}"
- end
-
delegate :quote_column_name, :quote_table_name, :quote_default_expression, :type_to_sql, to: :@conn
private :quote_column_name, :quote_table_name, :quote_default_expression, :type_to_sql
@@ -25,7 +21,7 @@ module ActiveRecord
def visit_AlterTable(o)
sql = "ALTER TABLE #{quote_table_name(o.name)} "
- sql << o.adds.map { |col| visit_AddColumn col }.join(' ')
+ sql << o.adds.map { |col| accept col }.join(' ')
sql << o.foreign_key_adds.map { |fk| visit_AddForeignKey fk }.join(' ')
sql << o.foreign_key_drops.map { |fk| visit_DropForeignKey fk }.join(' ')
end
@@ -37,6 +33,10 @@ module ActiveRecord
column_sql
end
+ def visit_AddColumnDefinition(o)
+ "ADD #{accept(o.column)}"
+ end
+
def visit_TableDefinition(o)
create_sql = "CREATE#{' TEMPORARY' if o.temporary} TABLE "
create_sql << "#{quote_table_name(o.name)} "
@@ -70,6 +70,7 @@ module ActiveRecord
column_options[:after] = o.after
column_options[:auto_increment] = o.auto_increment
column_options[:primary_key] = o.primary_key
+ column_options[:collation] = o.collation
column_options
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
index 4761024ad0..0ccf0c498b 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -15,13 +15,16 @@ module ActiveRecord
# are typically created by methods in TableDefinition, and added to the
# +columns+ attribute of said TableDefinition object, in order to be used
# for generating a number of table creation or table changing SQL statements.
- class ColumnDefinition < Struct.new(:name, :type, :limit, :precision, :scale, :default, :null, :first, :after, :auto_increment, :primary_key, :sql_type) #:nodoc:
+ class ColumnDefinition < Struct.new(:name, :type, :limit, :precision, :scale, :default, :null, :first, :after, :auto_increment, :primary_key, :collation, :sql_type) #:nodoc:
def primary_key?
primary_key || type.to_sym == :primary_key
end
end
+ class AddColumnDefinition < Struct.new(:column) # :nodoc:
+ end
+
class ChangeColumnDefinition < Struct.new(:column, :name) #:nodoc:
end
@@ -227,7 +230,7 @@ module ActiveRecord
# The +type+ parameter is normally one of the migrations native types,
# which is one of the following:
# <tt>:primary_key</tt>, <tt>:string</tt>, <tt>:text</tt>,
- # <tt>:integer</tt>, <tt>:float</tt>, <tt>:decimal</tt>,
+ # <tt>:integer</tt>, <tt>:bigint</tt>, <tt>:float</tt>, <tt>:decimal</tt>,
# <tt>:datetime</tt>, <tt>:time</tt>, <tt>:date</tt>,
# <tt>:binary</tt>, <tt>:boolean</tt>.
#
@@ -434,6 +437,7 @@ module ActiveRecord
column.after = options[:after]
column.auto_increment = options[:auto_increment]
column.primary_key = type == :primary_key || options[:primary_key]
+ column.collation = options[:collation]
column
end
@@ -476,7 +480,7 @@ module ActiveRecord
def add_column(name, type, options)
name = name.to_s
type = type.to_sym
- @adds << @td.new_column_definition(name, type, options)
+ @adds << AddColumnDefinition.new(@td.new_column_definition(name, type, options))
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
index 999cb0ec5a..deb014ad46 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
@@ -35,12 +35,16 @@ module ActiveRecord
default = schema_default(column) if column.has_default?
spec[:default] = default unless default.nil?
+ if collation = schema_collation(column)
+ spec[:collation] = collation
+ end
+
spec
end
# Lists the valid migration options
def migration_keys
- [:name, :limit, :precision, :scale, :default, :null]
+ [:name, :limit, :precision, :scale, :default, :null, :collation]
end
private
@@ -56,6 +60,10 @@ module ActiveRecord
type.type_cast_for_schema(default)
end
end
+
+ def schema_collation(column)
+ column.collation.inspect if column.collation
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
index 879a47f021..9004d86b04 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -14,6 +14,10 @@ module ActiveRecord
{}
end
+ def table_options(table_name)
+ nil
+ end
+
# Truncates a table alias according to the limits of the current adapter.
def table_alias_for(table_name)
table_name[0...table_alias_length].tr('.', '_')
@@ -930,13 +934,13 @@ module ActiveRecord
def add_index_options(table_name, column_name, options = {}) #:nodoc:
column_names = Array(column_name)
- index_name = index_name(table_name, column: column_names)
options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :using, :algorithm, :type)
- index_type = options[:unique] ? "UNIQUE" : ""
index_type = options[:type].to_s if options.key?(:type)
+ index_type ||= options[:unique] ? "UNIQUE" : ""
index_name = options[:name].to_s if options.key?(:name)
+ index_name ||= index_name(table_name, column: column_names)
max_index_length = options.fetch(:internal, false) ? index_name_length : allowed_index_name_length
if options.key?(:algorithm)
@@ -1049,7 +1053,7 @@ module ActiveRecord
end
end
- def validate_index_length!(table_name, new_name)
+ def validate_index_length!(table_name, new_name) # :nodoc:
if new_name.length > allowed_index_name_length
raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{allowed_index_name_length} characters"
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index ae42e8ef8d..0705c22a8c 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -387,8 +387,8 @@ module ActiveRecord
end
end
- def new_column(name, default, sql_type_metadata = nil, null = true)
- Column.new(name, default, sql_type_metadata, null)
+ def new_column(name, default, sql_type_metadata = nil, null = true, default_function = nil, collation = nil)
+ Column.new(name, default, sql_type_metadata, null, default_function, collation)
end
def lookup_cast_type(sql_type) # :nodoc:
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
index 76aee452ca..09aee1f467 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -14,7 +14,7 @@ module ActiveRecord
end
class ColumnDefinition < ActiveRecord::ConnectionAdapters::ColumnDefinition
- attr_accessor :charset, :collation
+ attr_accessor :charset
end
class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
@@ -28,7 +28,6 @@ module ActiveRecord
column.auto_increment = true
end
column.charset = options[:charset]
- column.collation = options[:collation]
column
end
@@ -44,10 +43,6 @@ module ActiveRecord
end
class SchemaCreation < AbstractAdapter::SchemaCreation
- def visit_AddColumn(o)
- add_column_position!(super, column_options(o))
- end
-
private
def visit_DropForeignKey(name)
@@ -67,6 +62,10 @@ module ActiveRecord
create_sql
end
+ def visit_AddColumnDefinition(o)
+ add_column_position!(super, column_options(o.column))
+ end
+
def visit_ChangeColumnDefinition(o)
change_column_sql = "CHANGE #{quote_column_name(o.name)} #{accept(o.column)}"
add_column_position!(change_column_sql, column_options(o.column))
@@ -75,7 +74,6 @@ module ActiveRecord
def column_options(o)
column_options = super
column_options[:charset] = o.charset
- column_options[:collation] = o.collation
column_options
end
@@ -99,8 +97,8 @@ module ActiveRecord
end
def index_in_create(table_name, column_name, options)
- index_name, index_type, index_columns, index_options, index_algorithm, index_using = @conn.add_index_options(table_name, column_name, options)
- "#{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})#{index_options} #{index_algorithm}"
+ index_name, index_type, index_columns, _, _, index_using = @conn.add_index_options(table_name, column_name, options)
+ "#{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns}) "
end
end
@@ -128,20 +126,20 @@ module ActiveRecord
spec = super
spec.delete(:precision) if /time/ === column.sql_type && column.precision == 0
spec.delete(:limit) if :boolean === column.type
+ spec
+ end
+
+ def schema_collation(column)
if column.collation && table_name = column.instance_variable_get(:@table_name)
@collation_cache ||= {}
@collation_cache[table_name] ||= select_one("SHOW TABLE STATUS LIKE '#{table_name}'")["Collation"]
- spec[:collation] = column.collation.inspect if column.collation != @collation_cache[table_name]
+ column.collation.inspect if column.collation != @collation_cache[table_name]
end
- spec
- end
-
- def migration_keys
- super + [:collation]
end
+ private :schema_collation
class Column < ConnectionAdapters::Column # :nodoc:
- delegate :strict, :collation, :extra, to: :sql_type_metadata, allow_nil: true
+ delegate :strict, :extra, to: :sql_type_metadata, allow_nil: true
def initialize(*)
super
@@ -195,12 +193,11 @@ module ActiveRecord
end
class MysqlTypeMetadata < DelegateClass(SqlTypeMetadata) # :nodoc:
- attr_reader :collation, :extra, :strict
+ attr_reader :extra, :strict
- def initialize(type_metadata, collation: "", extra: "", strict: false)
+ def initialize(type_metadata, extra: "", strict: false)
super(type_metadata)
@type_metadata = type_metadata
- @collation = collation
@extra = extra
@strict = strict
end
@@ -218,7 +215,7 @@ module ActiveRecord
protected
def attributes_for_hash
- [self.class, @type_metadata, collation, extra, strict]
+ [self.class, @type_metadata, extra, strict]
end
end
@@ -342,8 +339,8 @@ module ActiveRecord
raise NotImplementedError
end
- def new_column(field, default, sql_type_metadata = nil, null = true) # :nodoc:
- Column.new(field, default, sql_type_metadata, null)
+ def new_column(field, default, sql_type_metadata = nil, null = true, default_function = nil, collation = nil) # :nodoc:
+ Column.new(field, default, sql_type_metadata, null, default_function, collation)
end
# Must return the MySQL error number from the exception, if the exception has an
@@ -566,8 +563,8 @@ module ActiveRecord
each_hash(result).map do |field|
field_name = set_field_encoding(field[:Field])
sql_type = field[:Type]
- type_metadata = fetch_type_metadata(sql_type, field[:Collation], field[:Extra])
- new_column(field_name, field[:Default], type_metadata, field[:Null] == "YES")
+ type_metadata = fetch_type_metadata(sql_type, field[:Extra])
+ new_column(field_name, field[:Default], type_metadata, field[:Null] == "YES", nil, field[:Collation])
end
end
end
@@ -634,7 +631,7 @@ module ActiveRecord
change_column table_name, column_name, column.sql_type, :default => default
end
- def change_column_null(table_name, column_name, null, default = nil)
+ def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
column = column_for(table_name, column_name)
unless null || default.nil?
@@ -654,8 +651,8 @@ module ActiveRecord
end
def add_index(table_name, column_name, options = {}) #:nodoc:
- index_name, index_type, index_columns, index_options, index_algorithm, index_using = add_index_options(table_name, column_name, options)
- execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} ON #{quote_table_name(table_name)} (#{index_columns})#{index_options} #{index_algorithm}"
+ index_name, index_type, index_columns, _, index_algorithm, index_using = add_index_options(table_name, column_name, options)
+ execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} ON #{quote_table_name(table_name)} (#{index_columns}) #{index_algorithm}"
end
def foreign_keys(table_name)
@@ -686,6 +683,16 @@ module ActiveRecord
end
end
+ def table_options(table_name)
+ create_table_info = select_one("SHOW CREATE TABLE #{quote_table_name(table_name)}")["Create Table"]
+
+ # strip create_definitions and partition_options
+ raw_table_options = create_table_info.sub(/\A.*\n\) /m, '').sub(/\n\/\*!.*\*\/\n\z/m, '').strip
+
+ # strip AUTO_INCREMENT
+ raw_table_options.sub(/(ENGINE=\w+)(?: AUTO_INCREMENT=\d+)/, '\1')
+ end
+
# Maps logical Rails types to MySQL-specific data types.
def type_to_sql(type, limit = nil, precision = nil, scale = nil)
case type.to_s
@@ -826,8 +833,8 @@ module ActiveRecord
end
end
- def fetch_type_metadata(sql_type, collation = "", extra = "")
- MysqlTypeMetadata.new(super(sql_type), collation: collation, extra: extra, strict: strict_mode?)
+ def fetch_type_metadata(sql_type, extra = "")
+ MysqlTypeMetadata.new(super(sql_type), extra: extra, strict: strict_mode?)
end
# MySQL is too stupid to create a temporary table for use subquery, so we have
@@ -882,7 +889,7 @@ module ActiveRecord
def add_column_sql(table_name, column_name, type, options = {})
td = create_table_definition(table_name)
cd = td.new_column_definition(column_name, type, options)
- schema_creation.visit_AddColumn cd
+ schema_creation.accept(AddColumnDefinition.new(cd))
end
def change_column_sql(table_name, column_name, type, options = {})
@@ -924,8 +931,9 @@ module ActiveRecord
end
def add_index_sql(table_name, column_name, options = {})
- index_name, index_type, index_columns = add_index_options(table_name, column_name, options)
- "ADD #{index_type} INDEX #{index_name} (#{index_columns})"
+ index_name, index_type, index_columns, _, index_algorithm, index_using = add_index_options(table_name, column_name, options)
+ index_algorithm[0, 0] = ", " if index_algorithm.present?
+ "ADD #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})#{index_algorithm}"
end
def remove_index_sql(table_name, options = {})
diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb
index f4dda5154e..4b95b0681d 100644
--- a/activerecord/lib/active_record/connection_adapters/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/column.rb
@@ -12,7 +12,7 @@ module ActiveRecord
ISO_DATETIME = /\A(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)(\.\d+)?\z/
end
- attr_reader :name, :null, :sql_type_metadata, :default, :default_function
+ attr_reader :name, :null, :sql_type_metadata, :default, :default_function, :collation
delegate :precision, :scale, :limit, :type, :sql_type, to: :sql_type_metadata, allow_nil: true
@@ -22,12 +22,13 @@ module ActiveRecord
# +default+ is the type-casted default value, such as +new+ in <tt>sales_stage varchar(20) default 'new'</tt>.
# +sql_type_metadata+ is various information about the type of the column
# +null+ determines if this column allows +NULL+ values.
- def initialize(name, default, sql_type_metadata = nil, null = true, default_function = nil)
+ def initialize(name, default, sql_type_metadata = nil, null = true, default_function = nil, collation = nil)
@name = name
@sql_type_metadata = sql_type_metadata
@null = null
@default = default
@default_function = default_function
+ @collation = collation
@table_name = nil
end
@@ -60,7 +61,7 @@ module ActiveRecord
protected
def attributes_for_hash
- [self.class, name, default, sql_type_metadata, null, default_function]
+ [self.class, name, default, sql_type_metadata, null, default_function, collation]
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
index 21631be25c..e97e82f056 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
@@ -1,6 +1,6 @@
require 'active_record/connection_adapters/abstract_mysql_adapter'
-gem 'mysql2', '~> 0.3.13'
+gem 'mysql2', '~> 0.3.18'
require 'mysql2'
module ActiveRecord
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
index b7755c4593..f175730551 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
@@ -40,8 +40,7 @@ module ActiveRecord
PGconn.quote_ident(name.to_s)
end
- # Quote date/time values for use in SQL input. Includes microseconds
- # if the value is a Time responding to usec.
+ # Quote date/time values for use in SQL input.
def quoted_date(value) #:nodoc:
if value.year <= 0
bce_year = format("%04d", -value.year + 1)
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
index 168180cfd3..662c6b4d38 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
@@ -8,6 +8,13 @@ module ActiveRecord
o.sql_type = type_to_sql(o.type, o.limit, o.precision, o.scale, o.array)
super
end
+
+ def add_column_options!(sql, options)
+ if options[:collation]
+ sql << " COLLATE \"#{options[:collation]}\""
+ end
+ super
+ end
end
module SchemaStatements
@@ -100,6 +107,7 @@ module ActiveRecord
SQL
end
+ # Verifies existence of an index with a given name.
def index_name_exists?(table_name, index_name, default)
exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0
SELECT COUNT(*)
@@ -158,18 +166,18 @@ module ActiveRecord
# Returns the list of all column definitions for a table.
def columns(table_name)
# Limit, precision, and scale are all handled by the superclass.
- column_definitions(table_name).map do |column_name, type, default, notnull, oid, fmod|
+ column_definitions(table_name).map do |column_name, type, default, notnull, oid, fmod, collation|
oid = oid.to_i
fmod = fmod.to_i
type_metadata = fetch_type_metadata(column_name, type, oid, fmod)
default_value = extract_value_from_default(default)
default_function = extract_default_function(default_value, default)
- new_column(column_name, default_value, type_metadata, !notnull, default_function)
+ new_column(column_name, default_value, type_metadata, !notnull, default_function, collation)
end
end
- def new_column(name, default, sql_type_metadata = nil, null = true, default_function = nil) # :nodoc:
- PostgreSQLColumn.new(name, default, sql_type_metadata, null, default_function)
+ def new_column(name, default, sql_type_metadata = nil, null = true, default_function = nil, collation = nil) # :nodoc:
+ PostgreSQLColumn.new(name, default, sql_type_metadata, null, default_function, collation)
end
# Returns the current database name.
@@ -402,13 +410,15 @@ module ActiveRecord
super
end
- # Changes the column of a table.
- def change_column(table_name, column_name, type, options = {})
+ def change_column(table_name, column_name, type, options = {}) #:nodoc:
clear_cache!
quoted_table_name = quote_table_name(table_name)
quoted_column_name = quote_column_name(column_name)
sql_type = type_to_sql(type, options[:limit], options[:precision], options[:scale], options[:array])
sql = "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quoted_column_name} TYPE #{sql_type}"
+ if options[:collation]
+ sql << " COLLATE \"#{options[:collation]}\""
+ end
if options[:using]
sql << " USING #{options[:using]}"
elsif options[:cast_as]
@@ -437,7 +447,7 @@ module ActiveRecord
end
end
- def change_column_null(table_name, column_name, null, default = nil)
+ def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
clear_cache!
unless null || default.nil?
column = column_for(table_name, column_name)
@@ -462,6 +472,8 @@ module ActiveRecord
execute "DROP INDEX #{quote_table_name(index_name)}"
end
+ # Renames an index of a table. Raises error if length of new
+ # index name is greater than allowed limit.
def rename_index(table_name, old_name, new_name)
validate_index_length!(table_name, new_name)
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 332ac9d88c..7e15c2ab26 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -772,7 +772,9 @@ module ActiveRecord
def column_definitions(table_name) # :nodoc:
exec_query(<<-end_sql, 'SCHEMA').rows
SELECT a.attname, format_type(a.atttypid, a.atttypmod),
- pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod
+ pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
+ (SELECT c.collname FROM pg_collation c, pg_type t
+ WHERE c.oid = a.attcollation AND t.oid = a.atttypid AND a.attcollation <> t.typcollation)
FROM pg_attribute a LEFT JOIN pg_attrdef d
ON a.attrelid = d.adrelid AND a.attnum = d.adnum
WHERE a.attrelid = '#{quote_table_name(table_name)}'::regclass
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index 7e184dd510..3186769510 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -450,7 +450,7 @@ module ActiveRecord
end
end
- def change_column_null(table_name, column_name, null, default = nil)
+ def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
unless null || default.nil?
exec_query("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
end
diff --git a/activerecord/lib/active_record/errors.rb b/activerecord/lib/active_record/errors.rb
index 98aee77557..0f1759abaa 100644
--- a/activerecord/lib/active_record/errors.rb
+++ b/activerecord/lib/active_record/errors.rb
@@ -71,9 +71,9 @@ module ActiveRecord
class RecordNotDestroyed < ActiveRecordError
attr_reader :record
- def initialize(record)
+ def initialize(message, record = nil)
@record = record
- super()
+ super(message)
end
end
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index da8f4d027a..466175690e 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -148,7 +148,7 @@ module ActiveRecord
# Attributes marked as readonly are silently ignored if the record is
# being updated.
def save!(*args)
- create_or_update(*args) || raise(RecordNotSaved.new(nil, self))
+ create_or_update(*args) || raise(RecordNotSaved.new("Failed to save the record", self))
end
# Deletes the record in the database and freezes this instance to
@@ -193,7 +193,7 @@ module ActiveRecord
# and #destroy! raises ActiveRecord::RecordNotDestroyed.
# See ActiveRecord::Callbacks for further details.
def destroy!
- destroy || raise(ActiveRecord::RecordNotDestroyed, self)
+ destroy || raise(RecordNotDestroyed.new("Failed to destroy the record", self))
end
# Returns an instance of the specified +klass+ with the attributes of the
diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb
index eaeaf0321b..a4a986e6ed 100644
--- a/activerecord/lib/active_record/schema_dumper.rb
+++ b/activerecord/lib/active_record/schema_dumper.rb
@@ -131,6 +131,10 @@ HEADER
tbl.print ", id: false"
end
tbl.print ", force: :cascade"
+
+ table_options = @connection.table_options(table)
+ tbl.print ", options: #{table_options.inspect}" unless table_options.blank?
+
tbl.puts " do |t|"
# then dump all non-primary key columns
@@ -168,11 +172,11 @@ HEADER
tbl.puts
end
+ indexes(table, tbl)
+
tbl.puts " end"
tbl.puts
- indexes(table, tbl)
-
tbl.rewind
stream.print tbl.read
rescue => e
@@ -188,8 +192,7 @@ HEADER
if (indexes = @connection.indexes(table)).any?
add_index_statements = indexes.map do |index|
statement_parts = [
- "add_index #{remove_prefix_and_suffix(index.table).inspect}",
- index.columns.inspect,
+ "t.index #{index.columns.inspect}",
"name: #{index.name.inspect}",
]
statement_parts << 'unique: true' if index.unique
@@ -203,11 +206,10 @@ HEADER
statement_parts << "using: #{index.using.inspect}" if index.using
statement_parts << "type: #{index.type.inspect}" if index.type
- " #{statement_parts.join(', ')}"
+ " #{statement_parts.join(', ')}"
end
stream.puts add_index_statements.sort.join("\n")
- stream.puts
end
end
diff --git a/activerecord/lib/active_record/secure_token.rb b/activerecord/lib/active_record/secure_token.rb
index a3023a0cb4..ca11853da7 100644
--- a/activerecord/lib/active_record/secure_token.rb
+++ b/activerecord/lib/active_record/secure_token.rb
@@ -13,7 +13,7 @@ module ActiveRecord
#
# user = User.new
# user.save
- # user.token # => "4kUgL2pdQMSCQtjE"
+ # user.token # => "pX27zsMN2ViQKta1bGfLmVJE"
# user.auth_token # => "77TMHrHJFvFDwodq8w7Ev2m7"
# user.regenerate_token # => true
# user.regenerate_auth_token # => true
diff --git a/activerecord/test/cases/adapters/mysql/active_schema_test.rb b/activerecord/test/cases/adapters/mysql/active_schema_test.rb
index 6577d56240..57eb5d0e18 100644
--- a/activerecord/test/cases/adapters/mysql/active_schema_test.rb
+++ b/activerecord/test/cases/adapters/mysql/active_schema_test.rb
@@ -59,6 +59,43 @@ class ActiveSchemaTest < ActiveRecord::TestCase
assert_equal expected, add_index(:people, [:last_name, :first_name], :length => 15, :using => :btree)
end
+ def test_index_in_create
+ def (ActiveRecord::Base.connection).table_exists?(*); false; end
+
+ %w(SPATIAL FULLTEXT UNIQUE).each do |type|
+ expected = "CREATE TABLE `people` (#{type} INDEX `index_people_on_last_name` (`last_name`) ) ENGINE=InnoDB"
+ actual = ActiveRecord::Base.connection.create_table(:people, id: false) do |t|
+ t.index :last_name, type: type
+ end
+ assert_equal expected, actual
+ end
+
+ expected = "CREATE TABLE `people` ( INDEX `index_people_on_last_name` USING btree (`last_name`(10)) ) ENGINE=InnoDB"
+ actual = ActiveRecord::Base.connection.create_table(:people, id: false) do |t|
+ t.index :last_name, length: 10, using: :btree
+ end
+ assert_equal expected, actual
+ end
+
+ def test_index_in_bulk_change
+ def (ActiveRecord::Base.connection).table_exists?(*); true; end
+ def (ActiveRecord::Base.connection).index_name_exists?(*); false; end
+
+ %w(SPATIAL FULLTEXT UNIQUE).each do |type|
+ expected = "ALTER TABLE `people` ADD #{type} INDEX `index_people_on_last_name` (`last_name`)"
+ actual = ActiveRecord::Base.connection.change_table(:people, bulk: true) do |t|
+ t.index :last_name, type: type
+ end
+ assert_equal expected, actual
+ end
+
+ expected = "ALTER TABLE `peaple` ADD INDEX `index_peaple_on_last_name` USING btree (`last_name`(10)), ALGORITHM = COPY"
+ actual = ActiveRecord::Base.connection.change_table(:peaple, bulk: true) do |t|
+ t.index :last_name, length: 10, using: :btree, algorithm: :copy
+ end
+ assert_equal expected, actual
+ end
+
def test_drop_table
assert_equal "DROP TABLE `people`", drop_table(:people)
end
diff --git a/activerecord/test/cases/adapters/mysql/case_sensitivity_test.rb b/activerecord/test/cases/adapters/mysql/case_sensitivity_test.rb
index 340fc95503..345122b1ad 100644
--- a/activerecord/test/cases/adapters/mysql/case_sensitivity_test.rb
+++ b/activerecord/test/cases/adapters/mysql/case_sensitivity_test.rb
@@ -1,5 +1,4 @@
require "cases/helper"
-require 'models/person'
class MysqlCaseSensitivityTest < ActiveRecord::TestCase
class CollationTest < ActiveRecord::Base
diff --git a/activerecord/test/cases/adapters/mysql/table_options_test.rb b/activerecord/test/cases/adapters/mysql/table_options_test.rb
new file mode 100644
index 0000000000..0e5b0e8aec
--- /dev/null
+++ b/activerecord/test/cases/adapters/mysql/table_options_test.rb
@@ -0,0 +1,42 @@
+require "cases/helper"
+require 'support/schema_dumping_helper'
+
+class MysqlTableOptionsTest < ActiveRecord::TestCase
+ include SchemaDumpingHelper
+
+ def setup
+ @connection = ActiveRecord::Base.connection
+ end
+
+ def teardown
+ @connection.drop_table "mysql_table_options", if_exists: true
+ end
+
+ test "table options with ENGINE" do
+ @connection.create_table "mysql_table_options", force: true, options: "ENGINE=MyISAM"
+ output = dump_table_schema("mysql_table_options")
+ options = %r{create_table "mysql_table_options", force: :cascade, options: "(?<options>.*)"}.match(output)[:options]
+ assert_match %r{ENGINE=MyISAM}, options
+ end
+
+ test "table options with ROW_FORMAT" do
+ @connection.create_table "mysql_table_options", force: true, options: "ROW_FORMAT=REDUNDANT"
+ output = dump_table_schema("mysql_table_options")
+ options = %r{create_table "mysql_table_options", force: :cascade, options: "(?<options>.*)"}.match(output)[:options]
+ assert_match %r{ROW_FORMAT=REDUNDANT}, options
+ end
+
+ test "table options with CHARSET" do
+ @connection.create_table "mysql_table_options", force: true, options: "CHARSET=utf8mb4"
+ output = dump_table_schema("mysql_table_options")
+ options = %r{create_table "mysql_table_options", force: :cascade, options: "(?<options>.*)"}.match(output)[:options]
+ assert_match %r{CHARSET=utf8mb4}, options
+ end
+
+ test "table options with COLLATE" do
+ @connection.create_table "mysql_table_options", force: true, options: "COLLATE=utf8mb4_bin"
+ output = dump_table_schema("mysql_table_options")
+ options = %r{create_table "mysql_table_options", force: :cascade, options: "(?<options>.*)"}.match(output)[:options]
+ assert_match %r{COLLATE=utf8mb4_bin}, options
+ end
+end
diff --git a/activerecord/test/cases/adapters/mysql2/active_schema_test.rb b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb
index e87cd3886a..0ea556d4fa 100644
--- a/activerecord/test/cases/adapters/mysql2/active_schema_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb
@@ -59,6 +59,43 @@ class ActiveSchemaTest < ActiveRecord::TestCase
assert_equal expected, add_index(:people, [:last_name, :first_name], :length => 15, :using => :btree)
end
+ def test_index_in_create
+ def (ActiveRecord::Base.connection).table_exists?(*); false; end
+
+ %w(SPATIAL FULLTEXT UNIQUE).each do |type|
+ expected = "CREATE TABLE `people` (#{type} INDEX `index_people_on_last_name` (`last_name`) ) ENGINE=InnoDB"
+ actual = ActiveRecord::Base.connection.create_table(:people, id: false) do |t|
+ t.index :last_name, type: type
+ end
+ assert_equal expected, actual
+ end
+
+ expected = "CREATE TABLE `people` ( INDEX `index_people_on_last_name` USING btree (`last_name`(10)) ) ENGINE=InnoDB"
+ actual = ActiveRecord::Base.connection.create_table(:people, id: false) do |t|
+ t.index :last_name, length: 10, using: :btree
+ end
+ assert_equal expected, actual
+ end
+
+ def test_index_in_bulk_change
+ def (ActiveRecord::Base.connection).table_exists?(*); true; end
+ def (ActiveRecord::Base.connection).index_name_exists?(*); false; end
+
+ %w(SPATIAL FULLTEXT UNIQUE).each do |type|
+ expected = "ALTER TABLE `people` ADD #{type} INDEX `index_people_on_last_name` (`last_name`)"
+ actual = ActiveRecord::Base.connection.change_table(:people, bulk: true) do |t|
+ t.index :last_name, type: type
+ end
+ assert_equal expected, actual
+ end
+
+ expected = "ALTER TABLE `peaple` ADD INDEX `index_peaple_on_last_name` USING btree (`last_name`(10)), ALGORITHM = COPY"
+ actual = ActiveRecord::Base.connection.change_table(:peaple, bulk: true) do |t|
+ t.index :last_name, length: 10, using: :btree, algorithm: :copy
+ end
+ assert_equal expected, actual
+ end
+
def test_drop_table
assert_equal "DROP TABLE `people`", drop_table(:people)
end
diff --git a/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb b/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb
index 09bebf3071..ccf3d84a44 100644
--- a/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb
@@ -1,5 +1,4 @@
require "cases/helper"
-require 'models/person'
class Mysql2CaseSensitivityTest < ActiveRecord::TestCase
class CollationTest < ActiveRecord::Base
diff --git a/activerecord/test/cases/adapters/mysql2/table_options_test.rb b/activerecord/test/cases/adapters/mysql2/table_options_test.rb
new file mode 100644
index 0000000000..0e5b0e8aec
--- /dev/null
+++ b/activerecord/test/cases/adapters/mysql2/table_options_test.rb
@@ -0,0 +1,42 @@
+require "cases/helper"
+require 'support/schema_dumping_helper'
+
+class MysqlTableOptionsTest < ActiveRecord::TestCase
+ include SchemaDumpingHelper
+
+ def setup
+ @connection = ActiveRecord::Base.connection
+ end
+
+ def teardown
+ @connection.drop_table "mysql_table_options", if_exists: true
+ end
+
+ test "table options with ENGINE" do
+ @connection.create_table "mysql_table_options", force: true, options: "ENGINE=MyISAM"
+ output = dump_table_schema("mysql_table_options")
+ options = %r{create_table "mysql_table_options", force: :cascade, options: "(?<options>.*)"}.match(output)[:options]
+ assert_match %r{ENGINE=MyISAM}, options
+ end
+
+ test "table options with ROW_FORMAT" do
+ @connection.create_table "mysql_table_options", force: true, options: "ROW_FORMAT=REDUNDANT"
+ output = dump_table_schema("mysql_table_options")
+ options = %r{create_table "mysql_table_options", force: :cascade, options: "(?<options>.*)"}.match(output)[:options]
+ assert_match %r{ROW_FORMAT=REDUNDANT}, options
+ end
+
+ test "table options with CHARSET" do
+ @connection.create_table "mysql_table_options", force: true, options: "CHARSET=utf8mb4"
+ output = dump_table_schema("mysql_table_options")
+ options = %r{create_table "mysql_table_options", force: :cascade, options: "(?<options>.*)"}.match(output)[:options]
+ assert_match %r{CHARSET=utf8mb4}, options
+ end
+
+ test "table options with COLLATE" do
+ @connection.create_table "mysql_table_options", force: true, options: "COLLATE=utf8mb4_bin"
+ output = dump_table_schema("mysql_table_options")
+ options = %r{create_table "mysql_table_options", force: :cascade, options: "(?<options>.*)"}.match(output)[:options]
+ assert_match %r{COLLATE=utf8mb4_bin}, options
+ end
+end
diff --git a/activerecord/test/cases/adapters/postgresql/collation_test.rb b/activerecord/test/cases/adapters/postgresql/collation_test.rb
new file mode 100644
index 0000000000..17ef5f304c
--- /dev/null
+++ b/activerecord/test/cases/adapters/postgresql/collation_test.rb
@@ -0,0 +1,53 @@
+require "cases/helper"
+require 'support/schema_dumping_helper'
+
+class PostgresqlCollationTest < ActiveRecord::TestCase
+ include SchemaDumpingHelper
+
+ def setup
+ @connection = ActiveRecord::Base.connection
+ @connection.create_table :postgresql_collations, force: true do |t|
+ t.string :string_c, collation: 'C'
+ t.text :text_posix, collation: 'POSIX'
+ end
+ end
+
+ def teardown
+ @connection.drop_table :postgresql_collations, if_exists: true
+ end
+
+ test "string column with collation" do
+ column = @connection.columns(:postgresql_collations).find { |c| c.name == 'string_c' }
+ assert_equal :string, column.type
+ assert_equal 'C', column.collation
+ end
+
+ test "text column with collation" do
+ column = @connection.columns(:postgresql_collations).find { |c| c.name == 'text_posix' }
+ assert_equal :text, column.type
+ assert_equal 'POSIX', column.collation
+ end
+
+ test "add column with collation" do
+ @connection.add_column :postgresql_collations, :title, :string, collation: 'C'
+
+ column = @connection.columns(:postgresql_collations).find { |c| c.name == 'title' }
+ assert_equal :string, column.type
+ assert_equal 'C', column.collation
+ end
+
+ test "change column with collation" do
+ @connection.add_column :postgresql_collations, :description, :string
+ @connection.change_column :postgresql_collations, :description, :text, collation: 'POSIX'
+
+ column = @connection.columns(:postgresql_collations).find { |c| c.name == 'description' }
+ assert_equal :text, column.type
+ assert_equal 'POSIX', column.collation
+ end
+
+ test "schema dump includes collation" do
+ output = dump_table_schema("postgresql_collations")
+ assert_match %r{t.string\s+"string_c",\s+collation: "C"$}, output
+ assert_match %r{t.text\s+"text_posix",\s+collation: "POSIX"$}, output
+ end
+end
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index 14cdf37f46..2c4e2a875c 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -2131,11 +2131,12 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
car = Car.create!
original_child = FailedBulb.create!(car: car)
- assert_raise(ActiveRecord::RecordNotDestroyed) do
+ error = assert_raise(ActiveRecord::RecordNotDestroyed) do
car.failed_bulbs = [FailedBulb.create!]
end
assert_equal [original_child], car.reload.failed_bulbs
+ assert_equal "Failed to destroy the record", error.message
end
test 'updates counter cache when default scope is given' do
diff --git a/activerecord/test/cases/callbacks_test.rb b/activerecord/test/cases/callbacks_test.rb
index 3ae4a6eade..73ac30e547 100644
--- a/activerecord/test/cases/callbacks_test.rb
+++ b/activerecord/test/cases/callbacks_test.rb
@@ -451,6 +451,7 @@ class CallbacksTest < ActiveRecord::TestCase
assert !david.save
exc = assert_raise(ActiveRecord::RecordNotSaved) { david.save! }
assert_equal exc.record, david
+ assert_equal "Failed to save the record", exc.message
end
david = ImmutableDeveloper.find(1)
@@ -494,6 +495,7 @@ class CallbacksTest < ActiveRecord::TestCase
assert !david.destroy
exc = assert_raise(ActiveRecord::RecordNotDestroyed) { david.destroy! }
assert_equal exc.record, david
+ assert_equal "Failed to destroy the record", exc.message
end
assert_not_nil ImmutableDeveloper.find_by_id(1)
diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb
index 63612e33af..e6f0fe6f75 100644
--- a/activerecord/test/cases/schema_dumper_test.rb
+++ b/activerecord/test/cases/schema_dumper_test.rb
@@ -168,24 +168,24 @@ class SchemaDumperTest < ActiveRecord::TestCase
end
def test_schema_dumps_index_columns_in_right_order
- index_definition = standard_dump.split(/\n/).grep(/add_index.*companies/).first.strip
+ index_definition = standard_dump.split(/\n/).grep(/t\.index.*company_index/).first.strip
if current_adapter?(:MysqlAdapter, :Mysql2Adapter, :PostgreSQLAdapter)
- assert_equal 'add_index "companies", ["firm_id", "type", "rating"], name: "company_index", using: :btree', index_definition
+ assert_equal 't.index ["firm_id", "type", "rating"], name: "company_index", using: :btree', index_definition
else
- assert_equal 'add_index "companies", ["firm_id", "type", "rating"], name: "company_index"', index_definition
+ assert_equal 't.index ["firm_id", "type", "rating"], name: "company_index"', index_definition
end
end
def test_schema_dumps_partial_indices
- index_definition = standard_dump.split(/\n/).grep(/add_index.*company_partial_index/).first.strip
+ index_definition = standard_dump.split(/\n/).grep(/t\.index.*company_partial_index/).first.strip
if current_adapter?(:PostgreSQLAdapter)
- assert_equal 'add_index "companies", ["firm_id", "type"], name: "company_partial_index", where: "(rating > 10)", using: :btree', index_definition
+ assert_equal 't.index ["firm_id", "type"], name: "company_partial_index", where: "(rating > 10)", using: :btree', index_definition
elsif current_adapter?(:MysqlAdapter, :Mysql2Adapter)
- assert_equal 'add_index "companies", ["firm_id", "type"], name: "company_partial_index", using: :btree', index_definition
+ assert_equal 't.index ["firm_id", "type"], name: "company_partial_index", using: :btree', index_definition
elsif current_adapter?(:SQLite3Adapter) && ActiveRecord::Base.connection.supports_partial_index?
- assert_equal 'add_index "companies", ["firm_id", "type"], name: "company_partial_index", where: "rating > 10"', index_definition
+ assert_equal 't.index ["firm_id", "type"], name: "company_partial_index", where: "rating > 10"', index_definition
else
- assert_equal 'add_index "companies", ["firm_id", "type"], name: "company_partial_index"', index_definition
+ assert_equal 't.index ["firm_id", "type"], name: "company_partial_index"', index_definition
end
end
@@ -232,8 +232,8 @@ class SchemaDumperTest < ActiveRecord::TestCase
def test_schema_dumps_index_type
output = standard_dump
- assert_match %r{add_index "key_tests", \["awesome"\], name: "index_key_tests_on_awesome", type: :fulltext}, output
- assert_match %r{add_index "key_tests", \["pizza"\], name: "index_key_tests_on_pizza", using: :btree}, output
+ assert_match %r{t\.index \["awesome"\], name: "index_key_tests_on_awesome", type: :fulltext}, output
+ assert_match %r{t\.index \["pizza"\], name: "index_key_tests_on_pizza", using: :btree}, output
end
end
diff --git a/activerecord/test/schema/postgresql_specific_schema.rb b/activerecord/test/schema/postgresql_specific_schema.rb
index 008503bc24..872fa595b4 100644
--- a/activerecord/test/schema/postgresql_specific_schema.rb
+++ b/activerecord/test/schema/postgresql_specific_schema.rb
@@ -1,5 +1,16 @@
ActiveRecord::Schema.define do
+ enable_extension!('uuid-ossp', ActiveRecord::Base.connection)
+
+ create_table :uuid_parents, id: :uuid, force: true do |t|
+ t.string :name
+ end
+
+ create_table :uuid_children, id: :uuid, force: true do |t|
+ t.string :name
+ t.uuid :uuid_parent_id
+ end
+
%w(postgresql_times postgresql_oids defaults postgresql_timestamp_with_zones
postgresql_partitioned_table postgresql_partitioned_table_parent).each do |table_name|
drop_table table_name, if_exists: true
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 7b42f8a4a5..66f8f1611d 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -6,20 +6,6 @@ ActiveRecord::Schema.define do
end
end
- #put adapter specific setup here
- case adapter_name
- when "PostgreSQL"
- enable_extension!('uuid-ossp', ActiveRecord::Base.connection)
- create_table :uuid_parents, id: :uuid, force: true do |t|
- t.string :name
- end
- create_table :uuid_children, id: :uuid, force: true do |t|
- t.string :name
- t.uuid :uuid_parent_id
- end
- end
-
-
# ------------------------------------------------------------------- #
# #
# Please keep these create table statements in alphabetical order #
diff --git a/guides/source/4_2_release_notes.md b/guides/source/4_2_release_notes.md
index d5b3766a5b..684bd286bc 100644
--- a/guides/source/4_2_release_notes.md
+++ b/guides/source/4_2_release_notes.md
@@ -214,8 +214,9 @@ end
Due to a [change in Rack](https://github.com/rack/rack/commit/28b014484a8ac0bbb388e7eaeeef159598ec64fc),
`rails server` now listens on `localhost` instead of `0.0.0.0` by default. This
-should have minimal impact on the standard development workflow as http://localhost:3000
-will continue to work as before on your own machine.
+should have minimal impact on the standard development workflow as both
+http://127.0.0.1:3000 and http://localhost:3000 will continue to work as before
+on your own machine.
However, with this change you will no longer be able to access the Rails
server from a different machine, for example if your development environment
diff --git a/guides/source/active_record_validations.md b/guides/source/active_record_validations.md
index d251c5c0b1..343b761e93 100644
--- a/guides/source/active_record_validations.md
+++ b/guides/source/active_record_validations.md
@@ -617,7 +617,7 @@ end
The validation happens by performing an SQL query into the model's table,
searching for an existing record with the same value in that attribute.
-There is a `:scope` option that you can use to specify other attributes that
+There is a `:scope` option that you can use to specify one or more attributes that
are used to limit the uniqueness check:
```ruby
diff --git a/guides/source/caching_with_rails.md b/guides/source/caching_with_rails.md
index 716beb9178..782406659d 100644
--- a/guides/source/caching_with_rails.md
+++ b/guides/source/caching_with_rails.md
@@ -22,7 +22,7 @@ fragment caching. Rails provides by default fragment caching. In order to use
page and action caching, you will need to add `actionpack-page_caching` and
`actionpack-action_caching` to your Gemfile.
-To start playing with caching you'll want to ensure that `config.action_controller.perform_caching` is set to `true`, if you're running in development mode. This flag is normally set in the corresponding `config/environments/*.rb` and caching is disabled by default for development and test, and enabled for production.
+To start playing with caching you'll want to ensure that `config.action_controller.perform_caching` is set to `true` if you're running in development mode. This flag is normally set in the corresponding `config/environments/*.rb` and caching is disabled by default for development and test, and enabled for production.
```ruby
config.action_controller.perform_caching = true
@@ -144,7 +144,7 @@ It's called "Russian Doll Caching" because it nests multiple fragments. The adva
### Low-Level Caching
-Sometimes you need to cache a particular value or query result, instead of caching view fragments. Rails caching mechanism works great for storing __any__ kind of information.
+Sometimes you need to cache a particular value or query result instead of caching view fragments. Rails' caching mechanism works great for storing __any__ kind of information.
The most efficient way to implement low-level caching is using the `Rails.cache.fetch` method. This method does both reading and writing to the cache. When passed only a single argument, the key is fetched and value from the cache is returned. If a block is passed, the result of the block will be cached to the given key and the result is returned.
@@ -160,7 +160,7 @@ class Product < ActiveRecord::Base
end
```
-NOTE: Notice that in this example we used `cache_key` method, so the resulting cache-key will be something like `products/233-20140225082222765838000/competing_price`. `cache_key` generates a string based on the model’s `id` and `updated_at` attributes. This is a common convention and has the benefit of invalidating the cache whenever the product is updated. In general, when you use low-level caching for instance level information, you need to generate a cache key.
+NOTE: Notice that in this example we used the `cache_key` method, so the resulting cache-key will be something like `products/233-20140225082222765838000/competing_price`. `cache_key` generates a string based on the model’s `id` and `updated_at` attributes. This is a common convention and has the benefit of invalidating the cache whenever the product is updated. In general, when you use low-level caching for instance level information, you need to generate a cache key.
### SQL Caching
@@ -219,7 +219,7 @@ There are some common options used by all cache implementations. These can be pa
* `:compress` - This option can be used to indicate that compression should be used in the cache. This can be useful for transferring large cache entries over a slow network.
-* `:compress_threshold` - This options is used in conjunction with the `:compress` option to indicate a threshold under which cache entries should not be compressed. This defaults to 16 kilobytes.
+* `:compress_threshold` - This option is used in conjunction with the `:compress` option to indicate a threshold under which cache entries should not be compressed. This defaults to 16 kilobytes.
* `:expires_in` - This option sets an expiration time in seconds for the cache entry when it will be automatically removed from the cache.
@@ -227,7 +227,7 @@ There are some common options used by all cache implementations. These can be pa
### ActiveSupport::Cache::MemoryStore
-This cache store keeps entries in memory in the same Ruby process. The cache store has a bounded size specified by the `:size` options to the initializer (default is 32Mb). When the cache exceeds the allotted size, a cleanup will occur and the least recently used entries will be removed.
+This cache store keeps entries in memory in the same Ruby process. The cache store has a bounded size specified by the `:size` option to the initializer (default is 32Mb). When the cache exceeds the allotted size, a cleanup will occur and the least recently used entries will be removed.
```ruby
config.cache_store = :memory_store, { size: 64.megabytes }
@@ -243,7 +243,7 @@ This cache store uses the file system to store entries. The path to the director
config.cache_store = :file_store, "/path/to/cache/directory"
```
-With this cache store, multiple server processes on the same host can share a cache. Servers processes running on different hosts could share a cache by using a shared file system, but that set up would not be ideal and is not recommended. The cache store is appropriate for low to medium traffic sites that are served off one or two hosts.
+With this cache store, multiple server processes on the same host can share a cache. Server processes running on different hosts could share a cache by using a shared file system, but that set up would not be ideal and is not recommended. The cache store is appropriate for low to medium traffic sites that are served off one or two hosts.
Note that the cache will grow until the disk is full unless you periodically clear out old entries.
@@ -255,7 +255,7 @@ This cache store uses Danga's `memcached` server to provide a centralized cache
When initializing the cache, you need to specify the addresses for all memcached servers in your cluster. If none is specified, it will assume memcached is running on the local host on the default port, but this is not an ideal set up for larger sites.
-The `write` and `fetch` methods on this cache accept two additional options that take advantage of features specific to memcached. You can specify `:raw` to send a value directly to the server with no serialization. The value must be a string or number. You can use memcached direct operation like `increment` and `decrement` only on raw values. You can also specify `:unless_exist` if you don't want memcached to overwrite an existing entry.
+The `write` and `fetch` methods on this cache accept two additional options that take advantage of features specific to memcached. You can specify `:raw` to send a value directly to the server with no serialization. The value must be a string or number. You can use memcached direct operations like `increment` and `decrement` only on raw values. You can also specify `:unless_exist` if you don't want memcached to overwrite an existing entry.
```ruby
config.cache_store = :mem_cache_store, "cache-1.example.com", "cache-2.example.com"
@@ -293,7 +293,7 @@ For more information about Ehcache for JRuby and Rails, see [http://ehcache.org/
### ActiveSupport::Cache::NullStore
-This cache store implementation is meant to be used only in development or test environments and it never stores anything. This can be very useful in development when you have code that interacts directly with `Rails.cache`, but caching may interfere with being able to see the results of code changes. With this cache store, all `fetch` and `read` operations will result in a miss.
+This cache store implementation is meant to be used only in development or test environments and it never stores anything. This can be very useful in development when you have code that interacts directly with `Rails.cache` but caching may interfere with being able to see the results of code changes. With this cache store, all `fetch` and `read` operations will result in a miss.
```ruby
config.cache_store = :null_store
@@ -303,7 +303,7 @@ config.cache_store = :null_store
You can create your own custom cache store by simply extending `ActiveSupport::Cache::Store` and implementing the appropriate methods. In this way, you can swap in any number of caching technologies into your Rails application.
-To use a custom cache store, simple set the cache store to a new instance of the class.
+To use a custom cache store, simply set the cache store to a new instance of the class.
```ruby
config.cache_store = MyCacheStore.new
@@ -311,7 +311,7 @@ config.cache_store = MyCacheStore.new
### Cache Keys
-The keys used in a cache can be any object that responds to either `:cache_key` or to `:to_param`. You can implement the `:cache_key` method on your classes if you need to generate custom keys. Active Record will generate keys based on the class name and record id.
+The keys used in a cache can be any object that responds to either `:cache_key` or `:to_param`. You can implement the `:cache_key` method on your classes if you need to generate custom keys. Active Record will generate keys based on the class name and record id.
You can use Hashes and Arrays of values as cache keys.
@@ -353,7 +353,7 @@ class ProductsController < ApplicationController
end
```
-Instead of an options hash, you can also simply pass in a model, Rails will use the `updated_at` and `cache_key` methods for setting `last_modified` and `etag`:
+Instead of an options hash, you can also simply pass in a model. Rails will use the `updated_at` and `cache_key` methods for setting `last_modified` and `etag`:
```ruby
class ProductsController < ApplicationController
diff --git a/guides/source/debugging_rails_applications.md b/guides/source/debugging_rails_applications.md
index c6863f68e6..ec3ac62b8c 100644
--- a/guides/source/debugging_rails_applications.md
+++ b/guides/source/debugging_rails_applications.md
@@ -17,7 +17,7 @@ After reading this guide, you will know:
View Helpers for Debugging
--------------------------
-One common task is to inspect the contents of a variable. In Rails, you can do this with three methods:
+One common task is to inspect the contents of a variable. Rails provides three different ways to do this:
* `debug`
* `to_yaml`
@@ -54,7 +54,7 @@ Title: Rails debugging guide
### `to_yaml`
-Displaying an instance variable, or any other object or method, in YAML format can be achieved this way:
+Alternatively, calling `to_yaml` on any object converts it to YAML. You can pass this converted object into the `simple_format` helper method to format the output. This is how `debug` does its magic.
```html+erb
<%= simple_format @article.to_yaml %>
@@ -64,9 +64,7 @@ Displaying an instance variable, or any other object or method, in YAML format c
</p>
```
-The `to_yaml` method converts the method to YAML format leaving it more readable, and then the `simple_format` helper is used to render each line as in the console. This is how `debug` method does its magic.
-
-As a result of this, you will have something like this in your view:
+The above code will render something like this:
```yaml
--- !ruby/object Article
@@ -94,7 +92,7 @@ Another useful method for displaying object values is `inspect`, especially when
</p>
```
-Will be rendered as follows:
+Will render:
```
[1, 2, 3, 4, 5]
@@ -109,9 +107,9 @@ It can also be useful to save information to log files at runtime. Rails maintai
### What is the Logger?
-Rails makes use of the `ActiveSupport::Logger` class to write log information. You can also substitute another logger such as `Log4r` if you wish.
+Rails makes use of the `ActiveSupport::Logger` class to write log information. Other loggers, such as `Log4r`, may also be substituted.
-You can specify an alternative logger in your `environment.rb` or any environment file:
+You can specify an alternative logger in `environment.rb` or any other environment file, for example:
```ruby
Rails.logger = Logger.new(STDOUT)
@@ -130,7 +128,7 @@ TIP: By default, each log is created under `Rails.root/log/` and the log file is
### Log Levels
When something is logged, it's printed into the corresponding log if the log
-level of the message is equal or higher than the configured log level. If you
+level of the message is equal to or higher than the configured log level. If you
want to know the current log level, you can call the `Rails.logger.level`
method.
@@ -143,7 +141,7 @@ config.log_level = :warn # In any environment initializer, or
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.
+This is useful when you want to log under development or staging without flooding your production log with unnecessary information.
TIP: The default Rails log level is `debug` in all environments.
@@ -207,7 +205,7 @@ Adding extra logging like this makes it easy to search for unexpected or unusual
When running multi-user, multi-account applications, it's often useful
to be able to filter the logs using some custom rules. `TaggedLogging`
-in Active Support helps in doing exactly that by stamping log lines with subdomains, request ids, and anything else to aid debugging such applications.
+in Active Support helps you do exactly that by stamping log lines with subdomains, request ids, and anything else to aid debugging such applications.
```ruby
logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT))
@@ -217,34 +215,33 @@ logger.tagged("BCX") { logger.tagged("Jason") { logger.info "Stuff" } } # Logs "
```
### Impact of Logs on Performance
-Logging will always have a small impact on performance of your rails app,
- particularly when logging to disk. However, there are a few subtleties:
+Logging will always have a small impact on the performance of your Rails app,
+ particularly when logging to disk. Additionally, there are a few subtleties:
Using the `:debug` level will have a greater performance penalty than `:fatal`,
as a far greater number of strings are being evaluated and written to the
log output (e.g. disk).
-Another potential pitfall is that if you have many calls to `Logger` like this
- in your code:
+Another potential pitfall is too many calls to `Logger` in your code:
```ruby
logger.debug "Person attributes hash: #{@person.attributes.inspect}"
```
-In the above example, There will be a performance impact even if the allowed
+In the above example, there will be a performance impact even if the allowed
output level doesn't include debug. The reason is that Ruby has to evaluate
these strings, which includes instantiating the somewhat heavy `String` object
-and interpolating the variables, and which takes time.
+and interpolating the variables.
Therefore, it's recommended to pass blocks to the logger methods, as these are
-only evaluated if the output level is the same or included in the allowed level
+only evaluated if the output level is the same as — or included in — the allowed level
(i.e. lazy loading). The same code rewritten would be:
```ruby
logger.debug {"Person attributes hash: #{@person.attributes.inspect}"}
```
-The contents of the block, and therefore the string interpolation, is only
-evaluated if debug is enabled. This performance savings is only really
+The contents of the block, and therefore the string interpolation, are only
+evaluated if debug is enabled. This performance savings are only really
noticeable with large amounts of logging, but it's a good practice to employ.
Debugging with the `byebug` gem
@@ -258,8 +255,7 @@ is your best companion.
The debugger can also help you if you want to learn about the Rails source code
but don't know where to start. Just debug any request to your application and
-use this guide to learn how to move from the code you have written deeper into
-Rails code.
+use this guide to learn how to move from the code you have written into the underlying Rails code.
### Setup
@@ -290,7 +286,7 @@ As soon as your application calls the `byebug` method, the debugger will be
started in a debugger shell inside the terminal window where you launched your
application server, and you will be placed at the debugger's prompt `(byebug)`.
Before the prompt, the code around the line that is about to be run will be
-displayed and the current line will be marked by '=>'. Like this:
+displayed and the current line will be marked by '=>', like this:
```
[1, 10] in /PathTo/project/app/controllers/articles_controller.rb
@@ -344,7 +340,7 @@ Processing by ArticlesController#index as HTML
(byebug)
```
-Now it's time to explore and dig into your application. A good place to start is
+Now it's time to explore your application. A good place to start is
by asking the debugger for help. Type: `help`
```
@@ -365,9 +361,9 @@ continue edit frame kill pp reload skip undisplay
TIP: To view the help menu for any command use `help <command-name>` at the
debugger prompt. For example: _`help list`_. You can abbreviate any debugging
command by supplying just enough letters to distinguish them from other
-commands, so you can also use `l` for the `list` command, for example.
+commands. For example, you can use `l` for the `list` command.
-To see the previous ten lines you should type `list-` (or `l-`)
+To see the previous ten lines you should type `list-` (or `l-`).
```
(byebug) l-
@@ -386,7 +382,7 @@ To see the previous ten lines you should type `list-` (or `l-`)
```
-This way you can move inside the file, being able to see the code above and over
+This way you can move inside the file and see the code above
the line where you added the `byebug` call. Finally, to see where you are in
the code again you can type `list=`
@@ -416,8 +412,7 @@ contexts as you go through the different parts of the stack.
The debugger creates a context when a stopping point or an event is reached. The
context has information about the suspended program which enables the debugger
to inspect the frame stack, evaluate variables from the perspective of the
-debugged program, and contains information about the place where the debugged
-program is stopped.
+debugged program, and know the place where the debugged program is stopped.
At any time you can call the `backtrace` command (or its alias `where`) to print
the backtrace of the application. This can be very helpful to know how you got
@@ -481,9 +476,8 @@ character and the number indicates the current thread of execution.
* `thread resume _n_` resumes thread _n_.
* `thread switch _n_` switches the current thread context to _n_.
-This command is very helpful, among other occasions, when you are debugging
-concurrent threads and need to verify that there are no race conditions in your
-code.
+This command is very helpful when you are debugging concurrent threads and need
+to verify that there are no race conditions in your code.
### Inspecting Variables
@@ -545,11 +539,11 @@ Now `@articles` is included in the instance variables, because the line defining
was executed.
TIP: You can also step into **irb** mode with the command `irb` (of course!).
-This way an irb session will be started within the context you invoked it. But
+This will start an irb session within the context you invoked it. But
be warned: this is an experimental feature.
The `var` method is the most convenient way to show variables and their values.
-Let's let `byebug` help us with it.
+Let's have `byebug` help us with it.
```
(byebug) help var
@@ -561,7 +555,7 @@ v[ar] l[ocal] show local variables
```
This is a great way to inspect the values of the current context variables. For
-example, to check that we have no local variables currently defined.
+example, to check that we have no local variables currently defined:
```
(byebug) var local
@@ -592,14 +586,14 @@ tracking the values of a variable while the execution goes on.
1: @articles = nil
```
-The variables inside the displaying list will be printed with their values after
+The variables inside the displayed list will be printed with their values after
you move in the stack. To stop displaying a variable use `undisplay _n_` where
_n_ is the variable number (1 in the last example).
### Step by Step
Now you should know where you are in the running trace and be able to print the
-available variables. But lets continue and move on with the application
+available variables. But let's continue and move on with the application
execution.
Use `step` (abbreviated `s`) to continue running your program until the next
@@ -656,8 +650,8 @@ Next went up a frame because previous frame finished
(byebug)
```
-If we use `step` in the same situation, we will literally go the next ruby
-instruction to be executed. In this case, the activesupport's `week` method.
+If we use `step` in the same situation, we will literally go to the next Ruby
+instruction to be executed. In this case, Active Support's `week` method.
```
(byebug) step
@@ -677,8 +671,7 @@ instruction to be executed. In this case, the activesupport's `week` method.
(byebug)
```
-This is one of the best ways to find bugs in your code, or perhaps in Ruby on
-Rails.
+This is one of the best ways to find bugs in your code.
### Breakpoints
@@ -814,7 +807,7 @@ controller. The console would be rendered next to your HTML content.
### Console
-Inside any controller action or view, you can then invoke the console by
+Inside any controller action or view, you can invoke the console by
calling the `console` method.
For example, in a controller:
@@ -840,7 +833,7 @@ This will render a console inside your view. You don't need to care about the
location of the `console` call; it won't be rendered on the spot of its
invocation but next to your HTML content.
-The console executes pure Ruby code. You can define and instantiate
+The console executes pure Ruby code: You can define and instantiate
custom classes, create new models and inspect variables.
NOTE: Only one console can be rendered per request. Otherwise `web-console`
@@ -865,7 +858,7 @@ to use it in production.
Debugging Memory Leaks
----------------------
-A Ruby application (on Rails or not), can leak memory - either in the Ruby code
+A Ruby application (on Rails or not), can leak memory — either in the Ruby code
or at the C code level.
In this section, you will learn how to find and fix such leaks by using tool
@@ -896,7 +889,7 @@ footnotes that give request information and link back to your source via
TextMate.
* [Query Trace](https://github.com/ruckus/active-record-query-trace/tree/master) Adds query
origin tracing to your logs.
-* [Query Reviewer](https://github.com/nesquena/query_reviewer) This rails plugin
+* [Query Reviewer](https://github.com/nesquena/query_reviewer) This Rails plugin
not only runs "EXPLAIN" before each of your select queries in development, but
provides a small DIV in the rendered output of each page with the summary of
warnings for each query that it analyzed.
@@ -908,7 +901,7 @@ standard Rails error page with a new one containing more contextual information,
like source code and variable inspection.
* [RailsPanel](https://github.com/dejan/rails_panel) Chrome extension for Rails
development that will end your tailing of development.log. Have all information
-about your Rails app requests in the browser - in the Developer Tools panel.
+about your Rails app requests in the browser — in the Developer Tools panel.
Provides insight to db/rendering/total times, parameter list, rendered views and
more.
diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md
index 0aa2152d56..49834fa8a2 100644
--- a/guides/source/upgrading_ruby_on_rails.md
+++ b/guides/source/upgrading_ruby_on_rails.md
@@ -883,6 +883,20 @@ this gem such as `whitelist_attributes` or `mass_assignment_sanitizer` options.
* To re-enable the old finders, you can use the [activerecord-deprecated_finders gem](https://github.com/rails/activerecord-deprecated_finders).
+* Rails 4.0 has changed to default join table for `has_and_belongs_to_many` relations to strip the common prefix off the second table name. Any existing `has_and_belongs_to_many` relationship between models with a common prefix must be specified with the `join_table` option. For example:
+
+```ruby
+CatalogCategory < ActiveRecord::Base
+ has_and_belongs_to_many :catalog_products, join_table: 'catalog_categories_catalog_products'
+end
+
+CatalogProduct < ActiveRecord::Base
+ has_and_belongs_to_many :catalog_categories, join_table: 'catalog_categories_catalog_products'
+end
+```
+
+* Note that the the prefix takes scopes into account as well, so relations between `Catalog::Category` and `Catalog::Product` or `Catalog::Category` and `CatalogProduct` need to be updated similarly.
+
### Active Resource
Rails 4.0 extracted Active Resource to its own gem. If you still need the feature you can add the [Active Resource gem](https://github.com/rails/activeresource) in your Gemfile.
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index 43eee9f442..fb3953f866 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -77,7 +77,7 @@
*Andrew White*
-* Don't remove all line endings from routes.rb when revoking scaffold.
+* Don't remove all line endings from `config/routes.rb` when revoking scaffold.
Fixes #15913.
diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb
index 83cee28fa3..db75836b8a 100644
--- a/railties/lib/rails/engine.rb
+++ b/railties/lib/rails/engine.rb
@@ -405,7 +405,7 @@ module Rails
end
end
- # Finds engine with given path
+ # Finds engine with given path.
def find(path)
expanded_path = File.expand_path path
Rails::Engine.subclasses.each do |klass|
@@ -559,7 +559,7 @@ module Rails
# and the load_once paths.
#
# This needs to be an initializer, since it needs to run once
- # per engine and get the engine as a block parameter
+ # per engine and get the engine as a block parameter.
initializer :set_autoload_paths, before: :bootstrap_hook do
ActiveSupport::Dependencies.autoload_paths.unshift(*_all_autoload_paths)
ActiveSupport::Dependencies.autoload_once_paths.unshift(*_all_autoload_once_paths)
diff --git a/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb b/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb
index 8d825ae7b0..76313575e9 100644
--- a/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb
+++ b/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb
@@ -9,7 +9,6 @@ class <%= controller_class_name %>ControllerTest < ActionController::TestCase
test "should get index" do
get :index
assert_response :success
- assert_not_nil assigns(:<%= table_name %>)
end
test "should get new" do
@@ -22,7 +21,7 @@ class <%= controller_class_name %>ControllerTest < ActionController::TestCase
post :create, params: { <%= "#{singular_table_name}: { #{attributes_hash} }" %> }
end
- assert_redirected_to <%= singular_table_name %>_path(assigns(:<%= singular_table_name %>))
+ assert_redirected_to <%= singular_table_name %>_path(<%= class_name %>.last)
end
test "should show <%= singular_table_name %>" do
@@ -37,7 +36,7 @@ class <%= controller_class_name %>ControllerTest < ActionController::TestCase
test "should update <%= singular_table_name %>" do
patch :update, params: { id: <%= "@#{singular_table_name}" %>, <%= "#{singular_table_name}: { #{attributes_hash} }" %> }
- assert_redirected_to <%= singular_table_name %>_path(assigns(:<%= singular_table_name %>))
+ assert_redirected_to <%= singular_table_name %>_path(<%= "@#{singular_table_name}" %>)
end
test "should destroy <%= singular_table_name %>" do
diff --git a/railties/lib/rails/templates/rails/mailers/index.html.erb b/railties/lib/rails/templates/rails/mailers/index.html.erb
index c4c9757d57..000930c039 100644
--- a/railties/lib/rails/templates/rails/mailers/index.html.erb
+++ b/railties/lib/rails/templates/rails/mailers/index.html.erb
@@ -1,8 +1,8 @@
<% @previews.each do |preview| %>
-<h3><%= link_to preview.preview_name.titleize, "/rails/mailers/#{preview.preview_name}" %></h3>
+<h3><%= link_to preview.preview_name.titleize, url_for(controller: "rails/mailers", action: "preview", path: preview.preview_name) %></h3>
<ul>
<% preview.emails.each do |email| %>
-<li><%= link_to email, "/rails/mailers/#{preview.preview_name}/#{email}" %></li>
+<li><%= link_to email, url_for(controller: "rails/mailers", action: "preview", path: "#{preview.preview_name}/#{email}") %></li>
<% end %>
</ul>
<% end %>
diff --git a/railties/lib/rails/templates/rails/mailers/mailer.html.erb b/railties/lib/rails/templates/rails/mailers/mailer.html.erb
index 607c8d1677..c12ead0f90 100644
--- a/railties/lib/rails/templates/rails/mailers/mailer.html.erb
+++ b/railties/lib/rails/templates/rails/mailers/mailer.html.erb
@@ -1,6 +1,6 @@
<h3><%= @preview.preview_name.titleize %></h3>
<ul>
<% @preview.emails.each do |email| %>
-<li><%= link_to email, "/rails/mailers/#{@preview.preview_name}/#{email}" %></li>
+<li><%= link_to email, url_for(controller: "rails/mailers", action: "preview", path: "#{@preview.preview_name}/#{email}") %></li>
<% end %>
</ul>
diff --git a/railties/lib/rails/test_help.rb b/railties/lib/rails/test_help.rb
index 5cf44e6331..a83e39faee 100644
--- a/railties/lib/rails/test_help.rb
+++ b/railties/lib/rails/test_help.rb
@@ -27,13 +27,15 @@ if defined?(ActiveRecord::Base)
end
class ActionController::TestCase
- setup do
+ def before_setup
@routes = Rails.application.routes
+ super
end
end
class ActionDispatch::IntegrationTest
- setup do
+ def before_setup
@routes = Rails.application.routes
+ super
end
end
diff --git a/railties/lib/rails/test_unit/sub_test_task.rb b/railties/lib/rails/test_unit/sub_test_task.rb
deleted file mode 100644
index 6fa96d2ced..0000000000
--- a/railties/lib/rails/test_unit/sub_test_task.rb
+++ /dev/null
@@ -1,126 +0,0 @@
-require 'rake/testtask'
-
-module Rails
- class TestTask < Rake::TestTask # :nodoc: all
- # A utility class which is used primarily in "rails/test_unit/testing.rake"
- # to help define rake tasks corresponding to <tt>rake test</tt>.
- #
- # This class takes a TestInfo class and defines the appropriate rake task
- # based on the information, then invokes it.
- class TestCreator # :nodoc:
- def initialize(info)
- @info = info
- end
-
- def invoke_rake_task
- if @info.files.any?
- create_and_run_single_test
- reset_application_tasks
- else
- Rake::Task[ENV['TEST'] ? 'test:single' : 'test:run'].invoke
- end
- end
-
- private
-
- def create_and_run_single_test
- Rails::TestTask.new('test:single') { |t|
- t.test_files = @info.files
- }
- ENV['TESTOPTS'] ||= @info.opts
- Rake::Task['test:single'].invoke
- end
-
- def reset_application_tasks
- Rake.application.top_level_tasks.replace @info.tasks
- end
- end
-
- # This is a utility class used by the <tt>TestTask::TestCreator</tt> class.
- # This class takes a set of test tasks and checks to see if they correspond
- # to test files (or can be transformed into test files). Calling <tt>files</tt>
- # provides the set of test files and is used when initializing tests after
- # a call to <tt>rake test</tt>.
- class TestInfo # :nodoc:
- def initialize(tasks)
- @tasks = tasks
- @files = nil
- end
-
- def files
- @files ||= @tasks.map { |task|
- [task, translate(task)].find { |file| test_file?(file) }
- }.compact
- end
-
- def translate(file)
- if file =~ /^app\/(.*)$/
- "test/#{$1.sub(/\.rb$/, '')}_test.rb"
- else
- "test/#{file}_test.rb"
- end
- end
-
- def tasks
- @tasks - test_file_tasks - opt_names
- end
-
- def opts
- opts = opt_names
- if opts.any?
- "-n #{opts.join ' '}"
- end
- end
-
- private
-
- def test_file_tasks
- @tasks.find_all { |task|
- [task, translate(task)].any? { |file| test_file?(file) }
- }
- end
-
- def test_file?(file)
- file =~ /^test/ && File.file?(file) && !File.directory?(file)
- end
-
- def opt_names
- (@tasks - test_file_tasks).reject { |t| task_defined? t }
- end
-
- def task_defined?(task)
- Rake::Task.task_defined? task
- end
- end
-
- def self.test_creator(tasks)
- info = TestInfo.new(tasks)
- TestCreator.new(info)
- end
-
- def initialize(name = :test)
- super
- @libs << "test" # lib *and* test seem like a better default
- end
-
- def define
- task @name do
- if ENV['TESTOPTS']
- ARGV.replace Shellwords.split ENV['TESTOPTS']
- end
- libs = @libs - $LOAD_PATH
- $LOAD_PATH.unshift(*libs)
- file_list.each { |fl|
- FileList[fl].to_a.each { |f| require File.expand_path f }
- }
- end
- end
- end
-
- # Silence the default description to cut down on `rake -T` noise.
- class SubTestTask < Rake::TestTask # :nodoc:
- def desc(string)
- # Ignore the description.
- end
- end
-end
diff --git a/railties/test/application/configuration/base_test.rb b/railties/test/application/configuration/base_test.rb
deleted file mode 100644
index d6a82b139d..0000000000
--- a/railties/test/application/configuration/base_test.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-require 'isolation/abstract_unit'
-require 'rack/test'
-require 'env_helpers'
-
-module ApplicationTests
- module ConfigurationTests
- class BaseTest < ActiveSupport::TestCase
- def setup
- build_app
- boot_rails
- FileUtils.rm_rf("#{app_path}/config/environments")
- end
-
- def teardown
- teardown_app
- FileUtils.rm_rf(new_app) if File.directory?(new_app)
- end
-
- private
- def new_app
- File.expand_path("#{app_path}/../new_app")
- end
-
- def copy_app
- FileUtils.cp_r(app_path, new_app)
- end
-
- def app
- @app ||= Rails.application
- end
-
- def require_environment
- require "#{app_path}/config/environment"
- end
- end
- end
-end \ No newline at end of file
diff --git a/railties/test/application/configuration/custom_test.rb b/railties/test/application/configuration/custom_test.rb
index f8d22f6d97..f5c99d030e 100644
--- a/railties/test/application/configuration/custom_test.rb
+++ b/railties/test/application/configuration/custom_test.rb
@@ -1,22 +1,55 @@
-require 'application/configuration/base_test'
-
-class ApplicationTests::ConfigurationTests::CustomTest < ApplicationTests::ConfigurationTests::BaseTest
- test 'access custom configuration point' do
- add_to_config <<-RUBY
- config.x.payment_processing.schedule = :daily
- config.x.payment_processing.retries = 3
- config.x.super_debugger = true
- config.x.hyper_debugger = false
- config.x.nil_debugger = nil
- RUBY
- require_environment
-
- x = Rails.configuration.x
- assert_equal :daily, x.payment_processing.schedule
- assert_equal 3, x.payment_processing.retries
- assert_equal true, x.super_debugger
- assert_equal false, x.hyper_debugger
- assert_equal nil, x.nil_debugger
- assert_nil x.i_do_not_exist.zomg
+require 'isolation/abstract_unit'
+require 'env_helpers'
+
+module ApplicationTests
+ module ConfigurationTests
+ class CustomTest < ActiveSupport::TestCase
+ def setup
+ build_app
+ boot_rails
+ FileUtils.rm_rf("#{app_path}/config/environments")
+ end
+
+ def teardown
+ teardown_app
+ FileUtils.rm_rf(new_app) if File.directory?(new_app)
+ end
+
+ test 'access custom configuration point' do
+ add_to_config <<-RUBY
+ config.x.payment_processing.schedule = :daily
+ config.x.payment_processing.retries = 3
+ config.x.super_debugger = true
+ config.x.hyper_debugger = false
+ config.x.nil_debugger = nil
+ RUBY
+ require_environment
+
+ x = Rails.configuration.x
+ assert_equal :daily, x.payment_processing.schedule
+ assert_equal 3, x.payment_processing.retries
+ assert_equal true, x.super_debugger
+ assert_equal false, x.hyper_debugger
+ assert_equal nil, x.nil_debugger
+ assert_nil x.i_do_not_exist.zomg
+ end
+
+ private
+ def new_app
+ File.expand_path("#{app_path}/../new_app")
+ end
+
+ def copy_app
+ FileUtils.cp_r(app_path, new_app)
+ end
+
+ def app
+ @app ||= Rails.application
+ end
+
+ def require_environment
+ require "#{app_path}/config/environment"
+ end
+ end
end
end
diff --git a/railties/test/application/mailer_previews_test.rb b/railties/test/application/mailer_previews_test.rb
index 83501a7f11..1835d42c82 100644
--- a/railties/test/application/mailer_previews_test.rb
+++ b/railties/test/application/mailer_previews_test.rb
@@ -454,6 +454,36 @@ module ApplicationTests
assert_match '<option selected value="?part=text%2Fplain">View as plain-text email</option>', last_response.body
end
+ test "mailer previews create correct links when loaded on a subdirectory" do
+ mailer 'notifier', <<-RUBY
+ class Notifier < ActionMailer::Base
+ default from: "from@example.com"
+
+ def foo
+ mail to: "to@example.org"
+ end
+ end
+ RUBY
+
+ text_template 'notifier/foo', <<-RUBY
+ Hello, World!
+ RUBY
+
+ mailer_preview 'notifier', <<-RUBY
+ class NotifierPreview < ActionMailer::Preview
+ def foo
+ Notifier.foo
+ end
+ end
+ RUBY
+
+ app('development')
+
+ get "/rails/mailers", {}, 'SCRIPT_NAME' => '/my_app'
+ assert_match '<h3><a href="/my_app/rails/mailers/notifier">Notifier</a></h3>', last_response.body
+ assert_match '<li><a href="/my_app/rails/mailers/notifier/foo">foo</a></li>', last_response.body
+ end
+
private
def build_app
super
diff --git a/railties/test/application/rake_test.rb b/railties/test/application/rake_test.rb
index 0648b11813..de14f269df 100644
--- a/railties/test/application/rake_test.rb
+++ b/railties/test/application/rake_test.rb
@@ -190,7 +190,7 @@ module ApplicationTests
bundle exec rake db:migrate test`
end
- assert_match(/7 runs, 13 assertions, 0 failures, 0 errors/, output)
+ assert_match(/7 runs, 12 assertions, 0 failures, 0 errors/, output)
assert_no_match(/Errors running/, output)
end
@@ -203,7 +203,7 @@ module ApplicationTests
bundle exec rake db:migrate test`
end
- assert_match(/7 runs, 13 assertions, 0 failures, 0 errors/, output)
+ assert_match(/7 runs, 12 assertions, 0 failures, 0 errors/, output)
assert_no_match(/Errors running/, output)
end
diff --git a/railties/test/generators_test.rb b/railties/test/generators_test.rb
index fdcfb30090..ce75aba4eb 100644
--- a/railties/test/generators_test.rb
+++ b/railties/test/generators_test.rb
@@ -37,7 +37,7 @@ class GeneratorsTest < Rails::Generators::TestCase
def test_generator_multiple_suggestions
name = :tas
output = capture(:stdout){ Rails::Generators.invoke name }
- assert_match "Maybe you meant 'task', 'job' or 'assets'", output
+ assert_match "Maybe you meant 'task', 'job' or", output
end
def test_help_when_a_generator_with_required_arguments_is_invoked_without_arguments
diff --git a/railties/test/test_info_test.rb b/railties/test/test_info_test.rb
deleted file mode 100644
index b9c3a9c0c7..0000000000
--- a/railties/test/test_info_test.rb
+++ /dev/null
@@ -1,60 +0,0 @@
-require 'abstract_unit'
-require 'rails/test_unit/sub_test_task'
-
-module Rails
- class TestInfoTest < ActiveSupport::TestCase
- def test_test_files
- info = new_test_info ['test']
- assert_predicate info.files, :empty?
- assert_nil info.opts
- assert_equal ['test'], info.tasks
- end
-
- def test_with_file
- info = new_test_info ['test', __FILE__]
- assert_equal [__FILE__], info.files
- assert_nil info.opts
- assert_equal ['test'], info.tasks
- end
-
- def test_with_opts
- info = new_test_info ['test', __FILE__, '/foo/']
- assert_equal [__FILE__], info.files
- assert_equal '-n /foo/', info.opts
- assert_equal ['test'], info.tasks
- end
-
- def test_with_model_shorthand
- info = new_test_info ['test', 'models/foo', '/foo/']
-
- def info.test_file?(file)
- file == "test/models/foo_test.rb" || super
- end
-
- assert_equal ['test/models/foo_test.rb'], info.files
- assert_equal '-n /foo/', info.opts
- assert_equal ['test'], info.tasks
- end
-
- def test_with_model_path
- info = new_test_info ['test', 'app/models/foo.rb', '/foo/']
-
- def info.test_file?(file)
- file == "test/models/foo_test.rb" || super
- end
-
- assert_equal ['test/models/foo_test.rb'], info.files
- assert_equal '-n /foo/', info.opts
- assert_equal ['test'], info.tasks
- end
-
- private
- def new_test_info(tasks)
- Class.new(TestTask::TestInfo) {
- def task_defined?(task)
- task == "test"
- end
- }.new tasks
- end
- end
-end