aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTING.md40
-rw-r--r--Gemfile2
-rw-r--r--actionpack/CHANGELOG.md14
-rw-r--r--actionpack/lib/action_controller/metal/strong_parameters.rb12
-rw-r--r--actionpack/lib/action_controller/test_case.rb4
-rw-r--r--actionpack/lib/action_dispatch/testing/test_process.rb2
-rw-r--r--actionpack/test/controller/parameters/parameters_permit_test.rb8
-rw-r--r--activemodel/lib/active_model/errors.rb12
-rw-r--r--activemodel/lib/active_model/type/time.rb6
-rw-r--r--activemodel/lib/active_model/type/value.rb5
-rw-r--r--activemodel/test/cases/errors_test.rb10
-rw-r--r--activerecord/CHANGELOG.md9
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb4
-rw-r--r--activerecord/lib/active_record/persistence.rb2
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb38
-rw-r--r--activerecord/lib/active_record/sanitization.rb9
-rw-r--r--activerecord/test/cases/base_test.rb16
-rw-r--r--activerecord/test/cases/date_time_precision_test.rb37
-rw-r--r--activerecord/test/cases/finder_test.rb6
-rw-r--r--activerecord/test/cases/helper.rb2
-rw-r--r--activerecord/test/cases/locking_test.rb2
-rw-r--r--activerecord/test/cases/persistence_test.rb21
-rw-r--r--activerecord/test/cases/relations_test.rb7
-rw-r--r--activerecord/test/cases/sanitize_test.rb10
-rw-r--r--activerecord/test/cases/time_precision_test.rb34
-rw-r--r--activerecord/test/cases/touch_later_test.rb6
-rw-r--r--activesupport/CHANGELOG.md13
-rw-r--r--activesupport/lib/active_support/evented_file_update_checker.rb5
-rw-r--r--guides/source/active_support_core_extensions.md16
-rw-r--r--guides/source/configuring.md2
-rw-r--r--guides/source/engines.md8
-rw-r--r--guides/source/initialization.md12
-rw-r--r--guides/source/testing.md44
-rw-r--r--guides/source/working_with_javascript_in_rails.md4
-rw-r--r--railties/CHANGELOG.md10
-rw-r--r--railties/lib/rails/application/configuration.rb29
-rw-r--r--railties/lib/rails/command.rb2
-rw-r--r--railties/lib/rails/commands.rb2
-rw-r--r--railties/lib/rails/commands/commands_tasks.rb26
-rw-r--r--railties/lib/rails/commands/rake_proxy.rb34
-rw-r--r--railties/lib/rails/generators/rails/app/templates/Gemfile6
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt4
-rw-r--r--railties/lib/rails/generators/rails/app/templates/db/seeds.rb.tt4
-rw-r--r--railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb2
-rw-r--r--railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb2
-rw-r--r--railties/test/generators/app_generator_test.rb4
-rw-r--r--railties/test/generators/scaffold_controller_generator_test.rb2
47 files changed, 360 insertions, 189 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 699b6fd2d1..6871664a22 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,16 +1,42 @@
-Ruby on Rails is a volunteer effort. We encourage you to pitch in. [Join the team](http://contributors.rubyonrails.org)!
+## How to contribute to Ruby on Rails
-* If you want to submit a bug report please make sure to follow our [reporting guidelines](http://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html#reporting-an-issue).
+#### **Did you find a bug?**
-* If you want to submit a patch, please read the [Contributing to Ruby on Rails](http://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html) guide.
+* **Ensure the bug was not already reported** by searching on GitHub under [Issues](https://github.com/rails/rails/issues).
-* If you want to contribute to Rails documentation, please read the [Contributing to the Rails Documentation](http://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html#contributing-to-the-rails-documentation) section of the aforementioned guide.
+* If unable to find an open issue addressing the problem, [open a new one](https://github.com/rails/rails/issues/new). Be sure to include a **title and clear description**, as much relevant information as possible, and a **code sample** or an **executable test case** demonstrating the expected behavior that is not occurring.
-*We only accept bug reports and pull requests on GitHub*.
+* If possible, use the relevant bug report templates to create the issue. Simply copy the content of the appropriate template into a .rb file, make the necessary changes to demonstrate the issue, and **paste the content into the issue description**:
+ * [**Active Record** (models, database) issues](https://github.com/rails/rails/blob/master/guides/bug_report_templates/active_record_master.rb)
+ * [**Action Pack** (controllers, routing) issues](https://github.com/rails/rails/blob/master/guides/bug_report_templates/action_controller_master.rb)
+ * [**Generic template** for other issues](https://github.com/rails/rails/blob/master/guides/bug_report_templates/generic_master.rb)
-* 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).
+* For more detailed information on submitting a bug report and creating an issue, visit our [reporting guidelines](http://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html#reporting-an-issue).
-* 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.
+#### **Did you write a patch that fixes a bug?**
+
+* Open a new GitHub pull request with the patch.
+
+* Ensure the PR description clearly describes the problem and solution. Include the relevant issue number if applicable.
+
+* Before submitting, please read the [Contributing to Ruby on Rails](http://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html) guide to know more about coding conventions and benchmarks.
+
+#### **Do you intend to add a new feature or change an existing one?**
+
+* Suggest your change in the [rubyonrails-core mailing list](https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core) and start writing code.
+
+* Do not open an issue on GitHub until you have collected positive feedback about the change. GitHub issues are primarily intended for bug reports and fixes.
+
+#### **Do you have questions about the source code?**
+
+* Ask any question about how to use Ruby on Rails in the [rubyonrails-talk mailing list](https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-talk).
+
+#### **Do you want to contribute to the Rails documentation?**
+
+* Please read [Contributing to the Rails Documentation](http://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html#contributing-to-the-rails-documentation).
+
+</br>
+Ruby on Rails is a volunteer effort. We encourage you to pitch in and [join the team](http://contributors.rubyonrails.org)!
Thanks! :heart: :heart: :heart:
diff --git a/Gemfile b/Gemfile
index 3d580698de..932ac87e77 100644
--- a/Gemfile
+++ b/Gemfile
@@ -45,7 +45,7 @@ end
# Active Support.
gem 'dalli', '>= 2.2.1'
-gem 'listen', '~> 3.0.5'
+gem 'listen', '~> 3.0.5', require: false
# Active Job.
group :job do
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index 271a57a7ad..608512d846 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,3 +1,15 @@
+* `ActionController::TestCase` will be moved to it's own gem in Rails 5.1
+
+ With the speed improvements made to `ActionDispatch::IntegrationTest` we no
+ longer need to keep two separate code bases for testing controllers. In
+ Rails 5.1 `ActionController::TestCase` will be deprecated and moved into a
+ gem outside of Rails source.
+
+ This is a documentation deprecation so that going forward so new tests will use
+ `ActionDispatch::IntegrationTest` instead of `ActionController::TestCase`.
+
+ *Eileen M. Uchitelle*
+
* Add a `response_format` option to `ActionDispatch::DebugExceptions`
to configure the format of the response when errors occur in
development mode.
@@ -29,7 +41,7 @@
protect_from_forgery prepend: true
```
- * Eileen M. Uchitelle*
+ *Eileen M. Uchitelle*
* In url_for, never append a question mark to the URL when the query string
is empty anyway. (It used to do that when called like `url_for(controller:
diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb
index 8af94551cf..8bc3c271e2 100644
--- a/actionpack/lib/action_controller/metal/strong_parameters.rb
+++ b/actionpack/lib/action_controller/metal/strong_parameters.rb
@@ -162,8 +162,8 @@ module ActionController
end
end
- # Returns a safe +Hash+ representation of this parameter with all
- # unpermitted keys removed.
+ # Returns a safe <tt>ActiveSupport::HashWithIndifferentAccess</tt>
+ # representation of this parameter with all unpermitted keys removed.
#
# params = ActionController::Parameters.new({
# name: 'Senjougahara Hitagi',
@@ -175,15 +175,17 @@ module ActionController
# safe_params.to_h # => {"name"=>"Senjougahara Hitagi"}
def to_h
if permitted?
- @parameters.to_h
+ @parameters.deep_dup
else
slice(*self.class.always_permitted_parameters).permit!.to_h
end
end
- # Returns an unsafe, unfiltered +Hash+ representation of this parameter.
+ # Returns an unsafe, unfiltered
+ # <tt>ActiveSupport::HashWithIndifferentAccess</tt> representation of this
+ # parameter.
def to_unsafe_h
- @parameters.to_h
+ @parameters.deep_dup
end
alias_method :to_unsafe_hash, :to_unsafe_h
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index 442ffd6d7c..c55720859e 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -7,6 +7,9 @@ require 'action_controller/template_assertions'
require 'rails-dom-testing'
module ActionController
+ # :stopdoc:
+ # ActionController::TestCase will be deprecated and moved to a gem in Rails 5.1.
+ # Please use ActionDispatch::IntegrationTest going forward.
class TestRequest < ActionDispatch::TestRequest #:nodoc:
DEFAULT_ENV = ActionDispatch::TestRequest::DEFAULT_ENV.dup
DEFAULT_ENV.delete 'PATH_INFO'
@@ -658,4 +661,5 @@ module ActionController
include Behavior
end
+ # :startdoc:
end
diff --git a/actionpack/lib/action_dispatch/testing/test_process.rb b/actionpack/lib/action_dispatch/testing/test_process.rb
index c28d701b48..eca0439909 100644
--- a/actionpack/lib/action_dispatch/testing/test_process.rb
+++ b/actionpack/lib/action_dispatch/testing/test_process.rb
@@ -26,7 +26,7 @@ module ActionDispatch
@response.redirect_url
end
- # Shortcut for <tt>Rack::Test::UploadedFile.new(File.join(ActionController::TestCase.fixture_path, path), type)</tt>:
+ # Shortcut for <tt>Rack::Test::UploadedFile.new(File.join(ActionDispatch::IntegrationTest.fixture_path, path), type)</tt>:
#
# post :change_avatar, avatar: fixture_file_upload('files/spongebob.png', 'image/png')
#
diff --git a/actionpack/test/controller/parameters/parameters_permit_test.rb b/actionpack/test/controller/parameters/parameters_permit_test.rb
index 9f7d14e85d..87816515e7 100644
--- a/actionpack/test/controller/parameters/parameters_permit_test.rb
+++ b/actionpack/test/controller/parameters/parameters_permit_test.rb
@@ -256,7 +256,7 @@ class ParametersPermitTest < ActiveSupport::TestCase
end
test "to_h returns empty hash on unpermitted params" do
- assert @params.to_h.is_a? Hash
+ assert @params.to_h.is_a? ActiveSupport::HashWithIndifferentAccess
assert_not @params.to_h.is_a? ActionController::Parameters
assert @params.to_h.empty?
end
@@ -264,7 +264,7 @@ class ParametersPermitTest < ActiveSupport::TestCase
test "to_h returns converted hash on permitted params" do
@params.permit!
- assert @params.to_h.is_a? Hash
+ assert @params.to_h.is_a? ActiveSupport::HashWithIndifferentAccess
assert_not @params.to_h.is_a? ActionController::Parameters
end
@@ -273,7 +273,7 @@ class ParametersPermitTest < ActiveSupport::TestCase
ActionController::Parameters.permit_all_parameters = true
params = ActionController::Parameters.new(crab: "Senjougahara Hitagi")
- assert params.to_h.is_a? Hash
+ assert params.to_h.is_a? ActiveSupport::HashWithIndifferentAccess
assert_not @params.to_h.is_a? ActionController::Parameters
assert_equal({ "crab" => "Senjougahara Hitagi" }, params.to_h)
ensure
@@ -294,7 +294,7 @@ class ParametersPermitTest < ActiveSupport::TestCase
end
test "to_unsafe_h returns unfiltered params" do
- assert @params.to_h.is_a? Hash
+ assert @params.to_h.is_a? ActiveSupport::HashWithIndifferentAccess
assert_not @params.to_h.is_a? ActionController::Parameters
end
end
diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb
index 4726a68f69..ef6141a51d 100644
--- a/activemodel/lib/active_model/errors.rb
+++ b/activemodel/lib/active_model/errors.rb
@@ -81,6 +81,18 @@ module ActiveModel
super
end
+ # Copies the errors from <tt>other</tt>.
+ #
+ # other - The ActiveModel::Errors instance.
+ #
+ # Examples
+ #
+ # person.errors.copy!(other)
+ def copy!(other) # :nodoc:
+ @messages = other.messages.dup
+ @details = other.details.dup
+ end
+
# Clear the error messages.
#
# person.errors.full_messages # => ["name cannot be nil"]
diff --git a/activemodel/lib/active_model/type/time.rb b/activemodel/lib/active_model/type/time.rb
index 7101bad566..fe09f63a87 100644
--- a/activemodel/lib/active_model/type/time.rb
+++ b/activemodel/lib/active_model/type/time.rb
@@ -29,7 +29,11 @@ module ActiveModel
return value unless value.is_a?(::String)
return if value.empty?
- dummy_time_value = "2000-01-01 #{value}"
+ if value =~ /^2000-01-01/
+ dummy_time_value = value
+ else
+ dummy_time_value = "2000-01-01 #{value}"
+ end
fast_string_to_time(dummy_time_value) || begin
time_hash = ::Date._parse(dummy_time_value)
diff --git a/activemodel/lib/active_model/type/value.rb b/activemodel/lib/active_model/type/value.rb
index 5fea0561a6..9d1f267b41 100644
--- a/activemodel/lib/active_model/type/value.rb
+++ b/activemodel/lib/active_model/type/value.rb
@@ -90,6 +90,11 @@ module ActiveModel
scale == other.scale &&
limit == other.limit
end
+ alias eql? ==
+
+ def hash
+ [self.class, precision, scale, limit].hash
+ end
def assert_valid_value(*)
end
diff --git a/activemodel/test/cases/errors_test.rb b/activemodel/test/cases/errors_test.rb
index f6d171bec6..a5ac055033 100644
--- a/activemodel/test/cases/errors_test.rb
+++ b/activemodel/test/cases/errors_test.rb
@@ -410,4 +410,14 @@ class ErrorsTest < ActiveModel::TestCase
person.errors.clear
assert person.errors.details.empty?
end
+
+ test "copy errors" do
+ errors = ActiveModel::Errors.new(Person.new)
+ errors.add(:name, :invalid)
+ person = Person.new
+ person.errors.copy!(errors)
+
+ assert_equal [:name], person.errors.messages.keys
+ assert_equal [:name], person.errors.details.keys
+ end
end
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 56a3232ee9..8cedfd5277 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,12 @@
+* Use bind params for `limit` and `offset`. This will generate significantly
+ fewer prepared statements for common tasks like pagination. To support this
+ change, passing a string containing a comma to `limit` has been deprecated,
+ and passing an Arel node to `limit` is no longer supported.
+
+ Fixes #22250
+
+ *Sean Griffin*
+
* Introduce after_{create,update,delete}_commit callbacks.
Before:
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index 72ca909b02..163cbb875f 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -129,6 +129,10 @@ module ActiveRecord
true
end
+ def supports_datetime_with_precision?
+ true
+ end
+
def active?
@active != false
end
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index 9e566031b8..522c35252f 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -215,7 +215,7 @@ module ActiveRecord
became.instance_variable_set("@changed_attributes", attributes_changed_by_setter)
became.instance_variable_set("@new_record", new_record?)
became.instance_variable_set("@destroyed", destroyed?)
- became.instance_variable_set("@errors", errors)
+ became.errors.copy!(errors)
became
end
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index dbecb842b5..66f00c31e2 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -97,7 +97,22 @@ module ActiveRecord
end
def bound_attributes
- from_clause.binds + arel.bind_values + where_clause.binds + having_clause.binds
+ result = from_clause.binds + arel.bind_values + where_clause.binds + having_clause.binds
+ if limit_value && !string_containing_comma?(limit_value)
+ result << Attribute.with_cast_value(
+ "LIMIT".freeze,
+ connection.sanitize_limit(limit_value),
+ Type::Value.new,
+ )
+ end
+ if offset_value
+ result << Attribute.with_cast_value(
+ "OFFSET".freeze,
+ offset_value.to_i,
+ Type::Value.new,
+ )
+ end
+ result
end
def create_with_value # :nodoc:
@@ -677,6 +692,13 @@ module ActiveRecord
end
def limit!(value) # :nodoc:
+ if string_containing_comma?(value)
+ # Remove `string_containing_comma?` when removing this deprecation
+ ActiveSupport::Deprecation.warn(<<-WARNING)
+ Passing a string to limit in the form "1,2" is deprecated and will be
+ removed in Rails 5.1. Please call `offset` explicitly instead.
+ WARNING
+ end
self.limit_value = value
self
end
@@ -927,8 +949,14 @@ module ActiveRecord
arel.where(where_clause.ast) unless where_clause.empty?
arel.having(having_clause.ast) unless having_clause.empty?
- arel.take(connection.sanitize_limit(limit_value)) if limit_value
- arel.skip(offset_value.to_i) if offset_value
+ if limit_value
+ if string_containing_comma?(limit_value)
+ arel.take(connection.sanitize_limit(limit_value))
+ else
+ arel.take(Arel::Nodes::BindParam.new)
+ end
+ end
+ arel.skip(Arel::Nodes::BindParam.new) if offset_value
arel.group(*arel_columns(group_values.uniq.reject(&:blank?))) unless group_values.empty?
build_order(arel)
@@ -1177,5 +1205,9 @@ module ActiveRecord
def new_from_clause
Relation::FromClause.empty
end
+
+ def string_containing_comma?(value)
+ ::String === value && value.include?(",")
+ end
end
end
diff --git a/activerecord/lib/active_record/sanitization.rb b/activerecord/lib/active_record/sanitization.rb
index 0c15f45db9..4e89ba4dd1 100644
--- a/activerecord/lib/active_record/sanitization.rb
+++ b/activerecord/lib/active_record/sanitization.rb
@@ -18,6 +18,9 @@ module ActiveRecord
# sanitize_sql_for_conditions(["name=? and group_id=?", "foo'bar", 4])
# # => "name='foo''bar' and group_id=4"
#
+ # sanitize_sql_for_conditions(["name=:name and group_id=:group_id", name: "foo'bar", group_id: 4])
+ # # => "name='foo''bar' and group_id='4'"
+ #
# sanitize_sql_for_conditions(["name='%s' and group_id='%s'", "foo'bar", 4])
# # => "name='foo''bar' and group_id='4'"
#
@@ -40,6 +43,9 @@ module ActiveRecord
# sanitize_sql_for_assignment(["name=? and group_id=?", nil, 4])
# # => "name=NULL and group_id=4"
#
+ # sanitize_sql_for_assignment(["name=:name and group_id=:group_id", name: nil, group_id: 4])
+ # # => "name=NULL and group_id=4"
+ #
# Post.send(:sanitize_sql_for_assignment, { name: nil, group_id: 4 })
# # => "`posts`.`name` = NULL, `posts`.`group_id` = 4"
#
@@ -140,6 +146,9 @@ module ActiveRecord
# sanitize_sql_array(["name=? and group_id=?", "foo'bar", 4])
# # => "name='foo''bar' and group_id=4"
#
+ # sanitize_sql_array(["name=:name and group_id=:group_id", name: "foo'bar", group_id: 4])
+ # # => "name='foo''bar' and group_id=4"
+ #
# sanitize_sql_array(["name='%s' and group_id='%s'", "foo'bar", 4])
# # => "name='foo''bar' and group_id='4'"
def sanitize_sql_array(ary)
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index 3a9d60a79f..dc555caaff 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -112,7 +112,9 @@ class BasicsTest < ActiveRecord::TestCase
unless current_adapter?(:PostgreSQLAdapter, :OracleAdapter, :SQLServerAdapter, :FbAdapter)
def test_limit_with_comma
- assert Topic.limit("1,2").to_a
+ assert_deprecated do
+ assert Topic.limit("1,2").to_a
+ end
end
end
@@ -138,14 +140,10 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_limit_should_sanitize_sql_injection_for_limit_with_commas
- assert_raises(ArgumentError) do
- Topic.limit("1, 7 procedure help()").to_a
- end
- end
-
- unless current_adapter?(:MysqlAdapter, :Mysql2Adapter)
- def test_limit_should_allow_sql_literal
- assert_equal 1, Topic.limit(Arel.sql('2-1')).to_a.length
+ assert_deprecated do
+ assert_raises(ArgumentError) do
+ Topic.limit("1, 7 procedure help()").to_a
+ end
end
end
diff --git a/activerecord/test/cases/date_time_precision_test.rb b/activerecord/test/cases/date_time_precision_test.rb
index 698f1b852e..e996d142a2 100644
--- a/activerecord/test/cases/date_time_precision_test.rb
+++ b/activerecord/test/cases/date_time_precision_test.rb
@@ -10,6 +10,7 @@ class DateTimePrecisionTest < ActiveRecord::TestCase
setup do
@connection = ActiveRecord::Base.connection
+ Foo.reset_column_information
end
teardown do
@@ -20,24 +21,24 @@ class DateTimePrecisionTest < ActiveRecord::TestCase
@connection.create_table(:foos, force: true)
@connection.add_column :foos, :created_at, :datetime, precision: 0
@connection.add_column :foos, :updated_at, :datetime, precision: 5
- assert_equal 0, activerecord_column_option('foos', 'created_at', 'precision')
- assert_equal 5, activerecord_column_option('foos', 'updated_at', 'precision')
+ assert_equal 0, Foo.columns_hash['created_at'].precision
+ assert_equal 5, Foo.columns_hash['updated_at'].precision
end
def test_timestamps_helper_with_custom_precision
@connection.create_table(:foos, force: true) do |t|
t.timestamps precision: 4
end
- assert_equal 4, activerecord_column_option('foos', 'created_at', 'precision')
- assert_equal 4, activerecord_column_option('foos', 'updated_at', 'precision')
+ assert_equal 4, Foo.columns_hash['created_at'].precision
+ assert_equal 4, Foo.columns_hash['updated_at'].precision
end
def test_passing_precision_to_datetime_does_not_set_limit
@connection.create_table(:foos, force: true) do |t|
t.timestamps precision: 4
end
- assert_nil activerecord_column_option('foos', 'created_at', 'limit')
- assert_nil activerecord_column_option('foos', 'updated_at', 'limit')
+ assert_nil Foo.columns_hash['created_at'].limit
+ assert_nil Foo.columns_hash['updated_at'].limit
end
def test_invalid_datetime_precision_raises_error
@@ -48,14 +49,6 @@ class DateTimePrecisionTest < ActiveRecord::TestCase
end
end
- def test_database_agrees_with_activerecord_about_precision
- @connection.create_table(:foos, force: true) do |t|
- t.timestamps precision: 4
- end
- assert_equal 4, database_datetime_precision('foos', 'created_at')
- assert_equal 4, database_datetime_precision('foos', 'updated_at')
- end
-
def test_formatting_datetime_according_to_precision
@connection.create_table(:foos, force: true) do |t|
t.datetime :created_at, precision: 0
@@ -91,21 +84,5 @@ class DateTimePrecisionTest < ActiveRecord::TestCase
end
end
- private
-
- def database_datetime_precision(table_name, column_name)
- results = @connection.exec_query("SELECT column_name, datetime_precision FROM information_schema.columns WHERE table_name = '#{table_name}'")
- result = results.find do |result_hash|
- result_hash["column_name"] == column_name
- end
- result && result["datetime_precision"].to_i
- end
-
- def activerecord_column_option(tablename, column_name, option)
- result = @connection.columns(tablename).find do |column|
- column.name == column_name
- end
- result && result.send(option)
- end
end
end
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index 91214da048..73f5312eba 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -434,9 +434,9 @@ class FinderTest < ActiveRecord::TestCase
end
def test_take_and_first_and_last_with_integer_should_use_sql_limit
- assert_sql(/LIMIT 3|ROWNUM <= 3/) { Topic.take(3).entries }
- assert_sql(/LIMIT 2|ROWNUM <= 2/) { Topic.first(2).entries }
- assert_sql(/LIMIT 5|ROWNUM <= 5/) { Topic.last(5).entries }
+ assert_sql(/LIMIT|ROWNUM <=/) { Topic.take(3).entries }
+ assert_sql(/LIMIT|ROWNUM <=/) { Topic.first(2).entries }
+ assert_sql(/LIMIT|ROWNUM <=/) { Topic.last(5).entries }
end
def test_last_with_integer_and_order_should_keep_the_order
diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb
index d82a3040fc..07dbc8a53f 100644
--- a/activerecord/test/cases/helper.rb
+++ b/activerecord/test/cases/helper.rb
@@ -47,7 +47,7 @@ def in_memory_db?
end
def subsecond_precision_supported?
- !current_adapter?(:MysqlAdapter, :Mysql2Adapter) || ActiveRecord::Base.connection.version >= '5.6.4'
+ ActiveRecord::Base.connection.supports_datetime_with_precision?
end
def mysql_enforcing_gtid_consistency?
diff --git a/activerecord/test/cases/locking_test.rb b/activerecord/test/cases/locking_test.rb
index 2e1363334d..4fe76e563a 100644
--- a/activerecord/test/cases/locking_test.rb
+++ b/activerecord/test/cases/locking_test.rb
@@ -441,7 +441,7 @@ unless in_memory_db?
def test_lock_sending_custom_lock_statement
Person.transaction do
person = Person.find(1)
- assert_sql(/LIMIT 1 FOR SHARE NOWAIT/) do
+ assert_sql(/LIMIT \$\d FOR SHARE NOWAIT/) do
person.lock!('FOR SHARE NOWAIT')
end
end
diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb
index b2e59e3970..af15e63d9c 100644
--- a/activerecord/test/cases/persistence_test.rb
+++ b/activerecord/test/cases/persistence_test.rb
@@ -19,6 +19,8 @@ require 'models/person'
require 'models/pet'
require 'models/ship'
require 'models/toy'
+require 'models/admin'
+require 'models/admin/user'
require 'rexml/document'
class PersistenceTest < ActiveRecord::TestCase
@@ -161,7 +163,24 @@ class PersistenceTest < ActiveRecord::TestCase
assert !company.valid?
original_errors = company.errors
client = company.becomes(Client)
- assert_equal original_errors, client.errors
+ assert_equal original_errors.keys, client.errors.keys
+ end
+
+ def test_becomes_errors_base
+ child_class = Class.new(Admin::User) do
+ store_accessor :settings, :foo
+
+ def self.name; 'Admin::ChildUser'; end
+ end
+
+ admin = Admin::User.new
+ admin.errors.add :token, :invalid
+ child = admin.becomes(child_class)
+
+ assert_equal [:token], child.errors.keys
+ assert_nothing_raised do
+ child.errors.add :foo, :invalid
+ end
end
def test_dupd_becomes_persists_changes_from_the_original
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index cd23c1b3e1..7149c7d072 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -18,6 +18,7 @@ require 'models/minivan'
require 'models/aircraft'
require "models/possession"
require "models/reader"
+require "models/categorization"
class RelationTest < ActiveRecord::TestCase
fixtures :authors, :topics, :entrants, :developers, :companies, :developers_projects, :accounts, :categories, :categorizations, :posts, :comments,
@@ -918,6 +919,12 @@ class RelationTest < ActiveRecord::TestCase
assert authors.exists?(authors(:david).id)
end
+ def test_any_with_scope_on_hash_includes
+ post = authors(:david).posts.first
+ categories = Categorization.includes(author: :posts).where(posts: { id: post.id })
+ assert categories.exists?
+ end
+
def test_last
authors = Author.all
assert_equal authors(:bob), authors.last
diff --git a/activerecord/test/cases/sanitize_test.rb b/activerecord/test/cases/sanitize_test.rb
index 07970fb1c1..239f63d27b 100644
--- a/activerecord/test/cases/sanitize_test.rb
+++ b/activerecord/test/cases/sanitize_test.rb
@@ -25,6 +25,16 @@ class SanitizeTest < ActiveRecord::TestCase
assert_equal "name=#{quoted_bambi_and_thumper}", Binary.send(:sanitize_sql_array, ["name=?", "Bambi\nand\nThumper".mb_chars])
end
+ def test_sanitize_sql_array_handles_named_bind_variables
+ quoted_bambi = ActiveRecord::Base.connection.quote("Bambi")
+ assert_equal "name=#{quoted_bambi}", Binary.send(:sanitize_sql_array, ["name=:name", name: "Bambi"])
+ assert_equal "name=#{quoted_bambi} AND id=1", Binary.send(:sanitize_sql_array, ["name=:name AND id=:id", name: "Bambi", id: 1])
+
+ quoted_bambi_and_thumper = ActiveRecord::Base.connection.quote("Bambi\nand\nThumper")
+ assert_equal "name=#{quoted_bambi_and_thumper}", Binary.send(:sanitize_sql_array, ["name=:name", name: "Bambi\nand\nThumper"])
+ assert_equal "name=#{quoted_bambi_and_thumper} AND name2=#{quoted_bambi_and_thumper}", Binary.send(:sanitize_sql_array, ["name=:name AND name2=:name", name: "Bambi\nand\nThumper"])
+ end
+
def test_sanitize_sql_array_handles_relations
david = Author.create!(name: 'David')
david_posts = david.posts.select(:id)
diff --git a/activerecord/test/cases/time_precision_test.rb b/activerecord/test/cases/time_precision_test.rb
index ff7a81fe60..3b6e4dcc2b 100644
--- a/activerecord/test/cases/time_precision_test.rb
+++ b/activerecord/test/cases/time_precision_test.rb
@@ -10,6 +10,7 @@ class TimePrecisionTest < ActiveRecord::TestCase
setup do
@connection = ActiveRecord::Base.connection
+ Foo.reset_column_information
end
teardown do
@@ -20,8 +21,8 @@ class TimePrecisionTest < ActiveRecord::TestCase
@connection.create_table(:foos, force: true)
@connection.add_column :foos, :start, :time, precision: 3
@connection.add_column :foos, :finish, :time, precision: 6
- assert_equal 3, activerecord_column_option('foos', 'start', 'precision')
- assert_equal 6, activerecord_column_option('foos', 'finish', 'precision')
+ assert_equal 3, Foo.columns_hash['start'].precision
+ assert_equal 6, Foo.columns_hash['finish'].precision
end
def test_passing_precision_to_time_does_not_set_limit
@@ -29,8 +30,8 @@ class TimePrecisionTest < ActiveRecord::TestCase
t.time :start, precision: 3
t.time :finish, precision: 6
end
- assert_nil activerecord_column_option('foos', 'start', 'limit')
- assert_nil activerecord_column_option('foos', 'finish', 'limit')
+ assert_nil Foo.columns_hash['start'].limit
+ assert_nil Foo.columns_hash['finish'].limit
end
def test_invalid_time_precision_raises_error
@@ -42,15 +43,6 @@ class TimePrecisionTest < ActiveRecord::TestCase
end
end
- def test_database_agrees_with_activerecord_about_precision
- @connection.create_table(:foos, force: true) do |t|
- t.time :start, precision: 2
- t.time :finish, precision: 4
- end
- assert_equal 2, database_datetime_precision('foos', 'start')
- assert_equal 4, database_datetime_precision('foos', 'finish')
- end
-
def test_formatting_time_according_to_precision
@connection.create_table(:foos, force: true) do |t|
t.time :start, precision: 0
@@ -88,21 +80,5 @@ class TimePrecisionTest < ActiveRecord::TestCase
end
end
- private
-
- def database_datetime_precision(table_name, column_name)
- results = @connection.exec_query("SELECT column_name, datetime_precision FROM information_schema.columns WHERE table_name = '#{table_name}'")
- result = results.find do |result_hash|
- result_hash["column_name"] == column_name
- end
- result && result["datetime_precision"].to_i
- end
-
- def activerecord_column_option(tablename, column_name, option)
- result = @connection.columns(tablename).find do |column|
- column.name == column_name
- end
- result && result.send(option)
- end
end
end
diff --git a/activerecord/test/cases/touch_later_test.rb b/activerecord/test/cases/touch_later_test.rb
index 07dbb4b8f8..b47769eed7 100644
--- a/activerecord/test/cases/touch_later_test.rb
+++ b/activerecord/test/cases/touch_later_test.rb
@@ -100,9 +100,9 @@ class TouchLaterTest < ActiveRecord::TestCase
previous_parent_updated_at = nodes(:parent_a).updated_at
previous_child_updated_at = nodes(:child_one_of_a).updated_at
- travel 5.seconds
-
- Node.create! parent: nodes(:child_one_of_a), tree: trees(:root)
+ travel 5.seconds do
+ Node.create! parent: nodes(:child_one_of_a), tree: trees(:root)
+ end
assert_not_equal nodes(:child_one_of_a).reload.updated_at, previous_child_updated_at
assert_not_equal nodes(:parent_a).reload.updated_at, previous_parent_updated_at
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index d946fc1bbd..88558cde1c 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -7,13 +7,18 @@
*Michael Grosser*
-* Implements an evented file system monitor to asynchronously detect changes
- in the application source code, routes, locales, etc.
+* Implements an evented file watcher to asynchronously detect changes in the
+ application source code, routes, locales, etc.
- To opt-in load the [listen](https://github.com/guard/listen) gem in `Gemfile`:
+ This watcher is disabled by default, applications my enable it in the configuration:
+
+ # config/environments/development.rb
+ config.file_watcher = ActiveSupport::EventedFileUpdateChecker
+
+ This feature depends on the [listen](https://github.com/guard/listen) gem:
group :development do
- gem 'listen', '~> 3.0.4'
+ gem 'listen', '~> 3.0.5'
end
*Puneet Agarwal* and *Xavier Noria*
diff --git a/activesupport/lib/active_support/evented_file_update_checker.rb b/activesupport/lib/active_support/evented_file_update_checker.rb
index c1c30b1a86..315be85fb3 100644
--- a/activesupport/lib/active_support/evented_file_update_checker.rb
+++ b/activesupport/lib/active_support/evented_file_update_checker.rb
@@ -1,4 +1,3 @@
-require 'listen'
require 'set'
require 'pathname'
require 'concurrent/atomic/atomic_boolean'
@@ -19,6 +18,10 @@ module ActiveSupport
@lcsp = @ph.longest_common_subpath(@dirs.keys)
if (dtw = directories_to_watch).any?
+ # Loading listen triggers warnings. These are originated by a legit
+ # usage of attr_* macros for private attributes, but adds a lot of noise
+ # to our test suite. Thus, we lazy load it and disable warnings locally.
+ silence_warnings { require 'listen' }
Listen.to(*dtw, &method(:changed)).start
end
end
diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md
index 181dca4b71..06c5476d45 100644
--- a/guides/source/active_support_core_extensions.md
+++ b/guides/source/active_support_core_extensions.md
@@ -517,17 +517,17 @@ Extensions to `Module`
Using plain Ruby you can wrap methods with other methods, that's called _alias chaining_.
-For example, let's say you'd like params to be strings in functional tests, as they are in real requests, but still want the convenience of assigning integers and other kind of values. To accomplish that you could wrap `ActionController::TestCase#process` this way in `test/test_helper.rb`:
+For example, let's say you'd like params to be strings in functional tests, as they are in real requests, but still want the convenience of assigning integers and other kind of values. To accomplish that you could wrap `ActionDispatch::IntegrationTest#process` this way in `test/test_helper.rb`:
```ruby
-ActionController::TestCase.class_eval do
+ActionDispatch::IntegrationTest.class_eval do
# save a reference to the original process method
alias_method :original_process, :process
# now redefine process and delegate to original_process
- def process(action, params=nil, session=nil, flash=nil, http_method='GET')
+ def process('GET', path, params: nil, headers: nil, env: nil, xhr: false)
params = Hash[*params.map {|k, v| [k, v.to_s]}.flatten]
- original_process(action, params, session, flash, http_method)
+ original_process('GET', path, params: params)
end
end
```
@@ -537,10 +537,10 @@ That's the method `get`, `post`, etc., delegate the work to.
That technique has a risk, it could be the case that `:original_process` was taken. To try to avoid collisions people choose some label that characterizes what the chaining is about:
```ruby
-ActionController::TestCase.class_eval do
+ActionDispatch::IntegrationTest.class_eval do
def process_with_stringified_params(...)
params = Hash[*params.map {|k, v| [k, v.to_s]}.flatten]
- process_without_stringified_params(action, params, session, flash, http_method)
+ process_without_stringified_params(method, path, params: params)
end
alias_method :process_without_stringified_params, :process
alias_method :process, :process_with_stringified_params
@@ -550,10 +550,10 @@ end
The method `alias_method_chain` provides a shortcut for that pattern:
```ruby
-ActionController::TestCase.class_eval do
+ActionDispatch::IntegrationTest.class_eval do
def process_with_stringified_params(...)
params = Hash[*params.map {|k, v| [k, v.to_s]}.flatten]
- process_without_stringified_params(action, params, session, flash, http_method)
+ process_without_stringified_params(method, path, params: params)
end
alias_method_chain :process, :stringified_params
end
diff --git a/guides/source/configuring.md b/guides/source/configuring.md
index a286a7c5d2..ba2fb4c1cf 100644
--- a/guides/source/configuring.md
+++ b/guides/source/configuring.md
@@ -98,7 +98,7 @@ application. Accepts a valid week day symbol (e.g. `:monday`).
* `config.exceptions_app` sets the exceptions application invoked by the ShowException middleware when an exception happens. Defaults to `ActionDispatch::PublicExceptions.new(Rails.public_path)`.
-* `config.file_watcher` the class used to detect file updates in the filesystem when `config.reload_classes_only_on_change` is true. Must conform to `ActiveSupport::FileUpdateChecker` API.
+* `config.file_watcher` is the class used to detect file updates in the file system when `config.reload_classes_only_on_change` is true. Rails ships with `ActiveSupport::FileUpdateChecker`, the default, and `ActiveSupport::EventedFileUpdateChecker` (this one depends on the [listen](https://github.com/guard/listen) gem). Custom classes must conform to the `ActiveSupport::FileUpdateChecker` API.
* `config.filter_parameters` used for filtering out the parameters that
you don't want shown in the logs, such as passwords or credit card
diff --git a/guides/source/engines.md b/guides/source/engines.md
index 359796b1ff..a50ef9a95f 100644
--- a/guides/source/engines.md
+++ b/guides/source/engines.md
@@ -1033,9 +1033,9 @@ typical `GET` to a controller in a controller's functional test like this:
```ruby
module Blorgh
- class FooControllerTest < ActionController::TestCase
+ class FooControllerTest < ActionDispatch::IntegrationTest
def test_index
- get :index
+ get foos_url
...
end
end
@@ -1049,13 +1049,13 @@ in your setup code:
```ruby
module Blorgh
- class FooControllerTest < ActionController::TestCase
+ class FooControllerTest < ActionDispatch::IntegrationTest
setup do
@routes = Engine.routes
end
def test_index
- get :index
+ get foos_url
...
end
end
diff --git a/guides/source/initialization.md b/guides/source/initialization.md
index ebe1cb206a..7bf7eebb62 100644
--- a/guides/source/initialization.md
+++ b/guides/source/initialization.md
@@ -139,7 +139,8 @@ aliases = {
"c" => "console",
"s" => "server",
"db" => "dbconsole",
- "r" => "runner"
+ "r" => "runner",
+ "t" => "test"
}
command = ARGV.shift
@@ -158,19 +159,20 @@ defined here to find the matching command.
### `rails/commands/command_tasks.rb`
-When one types an incorrect rails command, the `run_command` is responsible for
-throwing an error message. If the command is valid, a method of the same name
-is called.
+When one types a valid Rails command, `run_command!` a method of the same name
+is called. If Rails doesn't recognize the command, it tries to run a Rake task
+of the same name.
```ruby
COMMAND_WHITELIST = %w(plugin generate destroy console server dbconsole application runner new version help)
def run_command!(command)
command = parse_command(command)
+
if COMMAND_WHITELIST.include?(command)
send(command)
else
- write_error_message(command)
+ run_rake_task(command)
end
end
```
diff --git a/guides/source/testing.md b/guides/source/testing.md
index 3bfbf4f7ff..58524fd6c5 100644
--- a/guides/source/testing.md
+++ b/guides/source/testing.md
@@ -330,7 +330,6 @@ You'll see the usage of some of these assertions in the next chapter.
All the basic assertions such as `assert_equal` defined in `Minitest::Assertions` are also available in the classes we use in our own test cases. In fact, Rails provides the following classes for you to inherit from:
* `ActiveSupport::TestCase`
-* `ActionController::TestCase`
* `ActionMailer::TestCase`
* `ActionView::TestCase`
* `ActionDispatch::IntegrationTest`
@@ -682,9 +681,9 @@ Let me take you through one such test, `test_should_get_index` from the file `ar
```ruby
# articles_controller_test.rb
-class ArticlesControllerTest < ActionController::TestCase
+class ArticlesControllerTest < ActionDispatch::IntegrationTest
test "should get index" do
- get :index
+ get '/articles'
assert_response :success
assert_includes @response.body, 'Articles'
end
@@ -697,7 +696,7 @@ and also ensuring that the right response body has been generated.
The `get` method kicks off the web request and populates the results into the response. It accepts 4 arguments:
* The action of the controller you are requesting.
- This can be in the form of a string or a symbol.
+ This can be in the form of a string or a route (i.e. `articles_url`).
* `params`: option with a hash of request parameters to pass into the action
(e.g. query string parameters or article variables).
@@ -717,7 +716,7 @@ get(:show, params: { id: 12 }, session: { user_id: 5 })
Another example: Calling the `:view` action, passing an `id` of 12 as the `params`, this time with no session, but with a flash message.
```ruby
-get(:view, params: { id: 12 }, flash: { message: 'booya!' })
+get(view_url, params: { id: 12 }, flash: { message: 'booya!' })
```
NOTE: If you try running `test_should_create_article` test from `articles_controller_test.rb` it will fail on account of the newly added model level validation and rightly so.
@@ -727,7 +726,7 @@ Let us modify `test_should_create_article` test in `articles_controller_test.rb`
```ruby
test "should create article" do
assert_difference('Article.count') do
- post :create, params: { article: { title: 'Some title' } }
+ post '/article', params: { article: { title: 'Some title' } }
end
assert_redirected_to article_path(Article.last)
@@ -758,7 +757,8 @@ To test AJAX requests, you can specify the `xhr: true` option to `get`, `post`,
```ruby
test "ajax request" do
- get :show, params: { id: articles(:first).id }, xhr: true
+ article = articles(:first)
+ get article_url(article), xhr: true
assert_equal 'hello world', @response.body
assert_equal "text/javascript", @response.content_type
@@ -799,11 +799,11 @@ can be set directly on the `@request` instance variable:
```ruby
# setting a HTTP Header
@request.headers["Accept"] = "text/plain, text/html"
-get :index # simulate the request with custom header
+get articles_url # simulate the request with custom header
# setting a CGI variable
@request.headers["HTTP_REFERER"] = "http://example.com/home"
-post :create # simulate the request with custom env variable
+post article_url # simulate the request with custom env variable
```
### Testing `flash` notices
@@ -818,7 +818,7 @@ Let's start by adding this assertion to our `test_should_create_article` test:
```ruby
test "should create article" do
assert_difference('Article.count') do
- post :create, params: { article: { title: 'Some title' } }
+ post article_url, params: { article: { title: 'Some title' } }
end
assert_redirected_to article_path(Article.last)
@@ -888,7 +888,7 @@ Let's write a test for the `:show` action:
```ruby
test "should show article" do
article = articles(:one)
- get :show, params: { id: article.id }
+ get '/article', params: { id: article.id }
assert_response :success
end
```
@@ -901,7 +901,7 @@ How about deleting an existing Article?
test "should destroy article" do
article = articles(:one)
assert_difference('Article.count', -1) do
- delete :destroy, params: { id: article.id }
+ delete article_url(article)
end
assert_redirected_to articles_path
@@ -913,7 +913,7 @@ We can also add a test for updating an existing Article.
```ruby
test "should update article" do
article = articles(:one)
- patch :update, params: { id: article.id, article: { title: "updated" } }
+ patch '/article', params: { id: article.id, article: { title: "updated" } }
assert_redirected_to article_path(article)
end
```
@@ -925,7 +925,7 @@ Our test should now look something like this, disregard the other tests we're le
```ruby
require 'test_helper'
-class ArticlesControllerTest < ActionController::TestCase
+class ArticlesControllerTest < ActionDispatch::IntegrationTest
# called before every single test
setup do
@article = articles(:one)
@@ -939,20 +939,20 @@ class ArticlesControllerTest < ActionController::TestCase
test "should show article" do
# Reuse the @article instance variable from setup
- get :show, params: { id: @article.id }
+ get article_url(@article)
assert_response :success
end
test "should destroy article" do
assert_difference('Article.count', -1) do
- delete :destroy, params: { id: @article.id }
+ delete article_url(@article)
end
assert_redirected_to articles_path
end
test "should update article" do
- patch :update, params: { id: @article.id, article: { title: "updated" } }
+ patch article_url(@article), params: { article: { title: "updated" } }
assert_redirected_to article_path(@article)
end
end
@@ -974,7 +974,7 @@ module SignInHelper
end
end
-class ActionController::TestCase
+class ActionDispatch::IntegrationTest
include SignInHelper
end
```
@@ -982,13 +982,13 @@ end
```ruby
require 'test_helper'
-class ProfileControllerTest < ActionController::TestCase
+class ProfileControllerTest < ActionDispatch::IntegrationTest
test "should show profile" do
# helper is now reusable from any controller test case
sign_in users(:david)
- get :show
+ get profile_url
assert_response :success
end
end
@@ -1186,10 +1186,10 @@ Functional testing for mailers involves more than just checking that the email b
```ruby
require 'test_helper'
-class UserControllerTest < ActionController::TestCase
+class UserControllerTest < ActionDispatch::IntegrationTest
test "invite friend" do
assert_difference 'ActionMailer::Base.deliveries.size', +1 do
- post :invite_friend, params: { email: 'friend@example.com' }
+ post invite_friend_url, params: { email: 'friend@example.com' }
end
invite_email = ActionMailer::Base.deliveries.last
diff --git a/guides/source/working_with_javascript_in_rails.md b/guides/source/working_with_javascript_in_rails.md
index 1c42ff2914..48fc6bc9c0 100644
--- a/guides/source/working_with_javascript_in_rails.md
+++ b/guides/source/working_with_javascript_in_rails.md
@@ -81,7 +81,7 @@ Awkward, right? We could pull the function definition out of the click handler,
and turn it into CoffeeScript:
```coffeescript
-paintIt = (element, backgroundColor, textColor) ->
+@paintIt = (element, backgroundColor, textColor) ->
element.style.backgroundColor = backgroundColor
if textColor?
element.style.color = textColor
@@ -107,7 +107,7 @@ attribute to our link, and then bind a handler to the click event of every link
that has that attribute:
```coffeescript
-paintIt = (element, backgroundColor, textColor) ->
+@paintIt = (element, backgroundColor, textColor) ->
element.style.backgroundColor = backgroundColor
if textColor?
element.style.color = textColor
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index e5ab31005e..b9d2db773a 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,3 +1,8 @@
+* The generated config file for the development environment includes a new
+ config line, commented out, showing how to enable the evented file watcher.
+
+ *Xavier Noria*
+
* `config.debug_exception_response_format` configures the format used
in responses when errors occur in development mode.
@@ -38,11 +43,6 @@
*Yuki Nishijima*
-* Generated `Gemfile`s for new applications include a new dependency on
- [listen](https://github.com/guard/listen) commented out.
-
- *Puneet Agarwal* and *Xavier Noria*
-
* Deprecate `serve_static_files` in favor of `public_file_server.enabled`.
Unifies the static asset options under `public_file_server`.
diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb
index a5550df0de..65cff1561a 100644
--- a/railties/lib/rails/application/configuration.rb
+++ b/railties/lib/rails/application/configuration.rb
@@ -44,7 +44,7 @@ module Rails
@railties_order = [:all]
@relative_url_root = ENV["RAILS_RELATIVE_URL_ROOT"]
@reload_classes_only_on_change = true
- @file_watcher = file_update_checker
+ @file_watcher = ActiveSupport::FileUpdateChecker
@exceptions_app = nil
@autoflush_log = true
@log_formatter = ActiveSupport::Logger::SimpleFormatter.new
@@ -191,26 +191,21 @@ module Rails
SourceAnnotationExtractor::Annotation
end
- private
- def file_update_checker
- ActiveSupport::FileUpdateChecker
+ class Custom #:nodoc:
+ def initialize
+ @configurations = Hash.new
end
- class Custom #:nodoc:
- def initialize
- @configurations = Hash.new
- end
-
- def method_missing(method, *args)
- if method =~ /=$/
- @configurations[$`.to_sym] = args.first
- else
- @configurations.fetch(method) {
- @configurations[method] = ActiveSupport::OrderedOptions.new
- }
- end
+ def method_missing(method, *args)
+ if method =~ /=$/
+ @configurations[$`.to_sym] = args.first
+ else
+ @configurations.fetch(method) {
+ @configurations[method] = ActiveSupport::OrderedOptions.new
+ }
end
end
+ end
end
end
end
diff --git a/railties/lib/rails/command.rb b/railties/lib/rails/command.rb
index 6587984b53..f7753cbb83 100644
--- a/railties/lib/rails/command.rb
+++ b/railties/lib/rails/command.rb
@@ -1,7 +1,7 @@
require 'rails/commands/commands_tasks'
module Rails
- class Command
+ class Command #:nodoc:
attr_reader :argv
def initialize(argv = [])
diff --git a/railties/lib/rails/commands.rb b/railties/lib/rails/commands.rb
index b9c4e02ca0..7627fcf5a0 100644
--- a/railties/lib/rails/commands.rb
+++ b/railties/lib/rails/commands.rb
@@ -7,7 +7,7 @@ aliases = {
"s" => "server",
"db" => "dbconsole",
"r" => "runner",
- "t" => "test",
+ "t" => "test"
}
command = ARGV.shift
diff --git a/railties/lib/rails/commands/commands_tasks.rb b/railties/lib/rails/commands/commands_tasks.rb
index 7e6b49e2a3..da3b9452d5 100644
--- a/railties/lib/rails/commands/commands_tasks.rb
+++ b/railties/lib/rails/commands/commands_tasks.rb
@@ -1,3 +1,5 @@
+require 'rails/commands/rake_proxy'
+
module Rails
# This is a class which takes in a rails command and initiates the appropriate
# initiation sequence.
@@ -5,6 +7,8 @@ module Rails
# Warning: This class mutates ARGV because some commands require manipulating
# it before they are run.
class CommandsTasks # :nodoc:
+ include Rails::RakeProxy
+
attr_reader :argv
HELP_MESSAGE = <<-EOT
@@ -20,14 +24,18 @@ The most common rails commands are:
new Create a new Rails application. "rails new my_app" creates a
new application called MyApp in "./my_app"
-In addition to those, there are:
- destroy Undo code generated with "generate" (short-cut alias: "d")
- plugin new Generates skeleton for developing a Rails plugin
- runner Run a piece of code in the application environment (short-cut alias: "r")
-
All commands can be run with -h (or --help) for more information.
+
+In addition to those commands, there are:
EOT
+ ADDITIONAL_COMMANDS = [
+ [ 'destroy', 'Undo code generated with "generate" (short-cut alias: "d")' ],
+ [ 'plugin new', 'Generates skeleton for developing a Rails plugin' ],
+ [ 'runner',
+ 'Run a piece of code in the application environment (short-cut alias: "r")' ]
+ ]
+
COMMAND_WHITELIST = %w(plugin generate destroy console server dbconsole runner new version help test)
def initialize(argv)
@@ -39,6 +47,8 @@ EOT
if COMMAND_WHITELIST.include?(command)
send(command)
+ else
+ run_rake_task(command)
end
end
@@ -109,6 +119,7 @@ EOT
def help
write_help_message
+ write_commands ADDITIONAL_COMMANDS + formatted_rake_tasks
end
private
@@ -150,6 +161,11 @@ EOT
puts HELP_MESSAGE
end
+ def write_commands(commands)
+ width = commands.map { |name, _| name.size }.max || 10
+ commands.each { |command| printf(" %-#{width}s %s\n", *command) }
+ end
+
def parse_command(command)
case command
when '--version', '-v'
diff --git a/railties/lib/rails/commands/rake_proxy.rb b/railties/lib/rails/commands/rake_proxy.rb
new file mode 100644
index 0000000000..f7d5df6b2f
--- /dev/null
+++ b/railties/lib/rails/commands/rake_proxy.rb
@@ -0,0 +1,34 @@
+require 'rake'
+require 'active_support'
+
+module Rails
+ module RakeProxy #:nodoc:
+ private
+ def run_rake_task(command)
+ ARGV.unshift(command) # Prepend the command, so Rake knows how to run it.
+
+ Rake.application.standard_exception_handling do
+ Rake.application.init('rails')
+ Rake.application.load_rakefile
+ Rake.application.top_level
+ end
+ end
+
+ def rake_tasks
+ return @rake_tasks if defined?(@rake_tasks)
+
+ ActiveSupport::Deprecation.silence do
+ require_application_and_environment!
+ end
+
+ Rake::TaskManager.record_task_metadata = true
+ Rake.application.instance_variable_set(:@name, 'rails')
+ Rails.application.load_tasks
+ @rake_tasks = Rake.application.tasks.select(&:comment)
+ end
+
+ def formatted_rake_tasks
+ rake_tasks.map { |t| [ t.name_with_args, t.comment ] }
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile b/railties/lib/rails/generators/rails/app/templates/Gemfile
index a0880d5166..2f7ffc055d 100644
--- a/railties/lib/rails/generators/rails/app/templates/Gemfile
+++ b/railties/lib/rails/generators/rails/app/templates/Gemfile
@@ -41,17 +41,13 @@ group :development do
<%- if options.dev? || options.edge? -%>
gem 'web-console', github: 'rails/web-console'
<%- else -%>
- gem 'web-console', '~> 2.0'
+ gem 'web-console', '~> 3.0'
<%- end -%>
<%- end -%>
<% if spring_install? -%>
# Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
gem 'spring'
<% end -%>
-
- # Loading the listen gem enables an evented file system monitor. Check
- # https://github.com/guard/listen#listen-adapters if on Windows or *BSD.
- # gem 'listen', '~> 3.0.5'
end
<% end -%>
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
index 2778dd8247..65c6aeb694 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
@@ -55,4 +55,8 @@ Rails.application.configure do
# Raises error for missing translations
# config.action_view.raise_on_missing_translations = true
+
+ # Use an evented file watcher to asynchronously detect changes in source code,
+ # routes, locales, etc. This feature depends on the listen gem.
+ # config.file_watcher = ActiveSupport::EventedFileUpdateChecker
end
diff --git a/railties/lib/rails/generators/rails/app/templates/db/seeds.rb.tt b/railties/lib/rails/generators/rails/app/templates/db/seeds.rb.tt
index 4edb1e857e..1a289be5e8 100644
--- a/railties/lib/rails/generators/rails/app/templates/db/seeds.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/db/seeds.rb.tt
@@ -3,5 +3,5 @@
#
# Examples:
#
-# cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }])
-# Mayor.create(name: 'Emanuel', city: cities.first)
+# movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }])
+# Character.create(name: 'Luke', movie: movies.first)
diff --git a/railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb b/railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb
index 17a86f376b..400afec6dc 100644
--- a/railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb
+++ b/railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb
@@ -52,7 +52,7 @@ class <%= controller_class_name %>Controller < ApplicationController
# Only allow a trusted parameter "white list" through.
def <%= "#{singular_table_name}_params" %>
<%- if attributes_names.empty? -%>
- params[:<%= singular_table_name %>]
+ params.fetch(:<%= singular_table_name %>, {})
<%- else -%>
params.require(:<%= singular_table_name %>).permit(<%= attributes_names.map { |name| ":#{name}" }.join(', ') %>)
<%- end -%>
diff --git a/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb b/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb
index f73e9a96ba..42b9e34274 100644
--- a/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb
+++ b/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb
@@ -59,7 +59,7 @@ class <%= controller_class_name %>Controller < ApplicationController
# Only allow a trusted parameter "white list" through.
def <%= "#{singular_table_name}_params" %>
<%- if attributes_names.empty? -%>
- params[:<%= singular_table_name %>]
+ params.fetch(:<%= singular_table_name %>, {})
<%- else -%>
params.require(:<%= singular_table_name %>).permit(<%= attributes_names.map { |name| ":#{name}" }.join(', ') %>)
<%- end -%>
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index 446fef562b..dd2e931c5c 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -505,7 +505,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
assert_file "Gemfile" do |content|
assert_match(/gem 'web-console',\s+github: 'rails\/web-console'/, content)
- assert_no_match(/gem 'web-console', '~> 2.0'/, content)
+ assert_no_match(/gem 'web-console', '~> 3.0'/, content)
end
end
@@ -514,7 +514,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
assert_file "Gemfile" do |content|
assert_match(/gem 'web-console',\s+github: 'rails\/web-console'/, content)
- assert_no_match(/gem 'web-console', '~> 2.0'/, content)
+ assert_no_match(/gem 'web-console', '~> 3.0'/, content)
end
end
diff --git a/railties/test/generators/scaffold_controller_generator_test.rb b/railties/test/generators/scaffold_controller_generator_test.rb
index 95ef853a11..b4ba101017 100644
--- a/railties/test/generators/scaffold_controller_generator_test.rb
+++ b/railties/test/generators/scaffold_controller_generator_test.rb
@@ -56,7 +56,7 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase
assert_file "app/controllers/users_controller.rb" do |content|
assert_match(/def user_params/, content)
- assert_match(/params\[:user\]/, content)
+ assert_match(/params\.fetch\(:user, \{\}\)/, content)
end
end