aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTING.md2
-rw-r--r--activemodel/lib/active_model/serializers/json.rb2
-rw-r--r--activerecord/lib/active_record/errors.rb4
-rw-r--r--activerecord/lib/active_record/null_relation.rb8
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb2
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb28
-rw-r--r--activerecord/test/cases/calculations_test.rb11
-rw-r--r--activerecord/test/cases/relations_test.rb5
-rw-r--r--guides/source/contributing_to_ruby_on_rails.md4
-rw-r--r--guides/source/getting_started.md2
-rw-r--r--guides/source/layout.html.erb4
-rw-r--r--railties/CHANGELOG.md4
-rw-r--r--railties/lib/rails/application/finisher.rb35
-rw-r--r--railties/test/application/loading_test.rb35
14 files changed, 108 insertions, 38 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 6d3cf072c1..19b7b638b6 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -4,6 +4,8 @@ Ruby on Rails is a volunteer effort. We encourage you to pitch in. [Join the tea
* 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.
+* 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.
+
*We only accept bug reports and pull requests in 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).
diff --git a/activemodel/lib/active_model/serializers/json.rb b/activemodel/lib/active_model/serializers/json.rb
index 9d984b7a18..05e2e089e5 100644
--- a/activemodel/lib/active_model/serializers/json.rb
+++ b/activemodel/lib/active_model/serializers/json.rb
@@ -109,7 +109,7 @@ module ActiveModel
#
# def attributes=(hash)
# hash.each do |key, value|
- # instance_variable_set("@#{key}", value)
+ # send("#{key}=", value)
# end
# end
#
diff --git a/activerecord/lib/active_record/errors.rb b/activerecord/lib/active_record/errors.rb
index 017b8bace6..7e38719811 100644
--- a/activerecord/lib/active_record/errors.rb
+++ b/activerecord/lib/active_record/errors.rb
@@ -69,10 +69,6 @@ module ActiveRecord
end
end
- # Raised when SQL statement is invalid and the application gets a blank result.
- class ThrowResult < ActiveRecordError
- end
-
# Defunct wrapper class kept for compatibility.
# +StatementInvalid+ wraps the original exception now.
class WrappedDatabaseException < StatementInvalid
diff --git a/activerecord/lib/active_record/null_relation.rb b/activerecord/lib/active_record/null_relation.rb
index 711fc8b883..d166f0dd66 100644
--- a/activerecord/lib/active_record/null_relation.rb
+++ b/activerecord/lib/active_record/null_relation.rb
@@ -39,7 +39,7 @@ module ActiveRecord
end
def to_sql
- @to_sql ||= ""
+ ""
end
def where_values_hash
@@ -55,7 +55,11 @@ module ActiveRecord
end
def calculate(_operation, _column_name, _options = {})
- nil
+ if _operation == :count
+ 0
+ else
+ nil
+ end
end
def exists?(_id = false)
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index e234e02032..ccb48247b7 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -106,8 +106,6 @@ module ActiveRecord
else
relation.calculate(operation, column_name, options)
end
- rescue ThrowResult
- 0
end
# Use <tt>pluck</tt> as a shortcut to select one or more attributes without
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index ba222aac93..f0edef58bf 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -160,8 +160,9 @@ module ActiveRecord
conditions = conditions.id if Base === conditions
return false if !conditions
- join_dependency = construct_join_dependency
- relation = construct_relation_for_association_find(join_dependency)
+ relation = construct_relation_for_association_find(construct_join_dependency)
+ return false if ActiveRecord::NullRelation === relation
+
relation = relation.except(:select, :order).select("1 AS one").limit(1)
case conditions
@@ -172,8 +173,6 @@ module ActiveRecord
end
connection.select_value(relation, "#{name} Exists", relation.bind_values)
- rescue ThrowResult
- false
end
# This method is called whenever no records are found with either a single
@@ -203,10 +202,12 @@ module ActiveRecord
def find_with_associations
join_dependency = construct_join_dependency
relation = construct_relation_for_association_find(join_dependency)
- rows = connection.select_all(relation, 'SQL', relation.bind_values.dup)
- join_dependency.instantiate(rows)
- rescue ThrowResult
- []
+ if ActiveRecord::NullRelation === relation
+ []
+ else
+ rows = connection.select_all(relation, 'SQL', relation.bind_values.dup)
+ join_dependency.instantiate(rows)
+ end
end
def construct_join_dependency(joins = [])
@@ -230,21 +231,22 @@ module ActiveRecord
if using_limitable_reflections?(join_dependency.reflections)
relation
else
- relation.where!(construct_limited_ids_condition(relation)) if relation.limit_value
+ if relation.limit_value
+ limited_ids = limited_ids_for(relation)
+ limited_ids.empty? ? relation.none! : relation.where!(table[primary_key].in(limited_ids))
+ end
relation.except(:limit, :offset)
end
end
- def construct_limited_ids_condition(relation)
+ def limited_ids_for(relation)
values = @klass.connection.columns_for_distinct(
"#{quoted_table_name}.#{quoted_primary_key}", relation.order_values)
relation = relation.except(:select).select(values).distinct!
id_rows = @klass.connection.select_all(relation.arel, 'SQL', relation.bind_values)
- ids_array = id_rows.map {|row| row[primary_key]}
-
- ids_array.empty? ? raise(ThrowResult) : table[primary_key].in(ids_array)
+ id_rows.map {|row| row[primary_key]}
end
def find_with_ids(*ids)
diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb
index 095b78c6c8..c8ac77984f 100644
--- a/activerecord/test/cases/calculations_test.rb
+++ b/activerecord/test/cases/calculations_test.rb
@@ -6,6 +6,7 @@ require 'models/edge'
require 'models/organization'
require 'models/possession'
require 'models/topic'
+require 'models/reply'
require 'models/minivan'
require 'models/speedometer'
require 'models/ship_part'
@@ -472,6 +473,11 @@ class CalculationsTest < ActiveRecord::TestCase
assert_equal [1,2,3,4], Topic.order(:id).pluck(:id)
end
+ def test_pluck_without_column_names
+ assert_equal [[1, "Firm", 1, nil, "37signals", nil, 1, nil, ""]],
+ Company.order(:id).limit(1).pluck
+ end
+
def test_pluck_type_cast
topic = topics(:first)
relation = Topic.where(:id => topic.id)
@@ -533,6 +539,11 @@ class CalculationsTest < ActiveRecord::TestCase
assert_equal Company.all.map(&:id).sort, Company.ids.sort
end
+ def test_pluck_with_includes_limit_and_empty_result
+ assert_equal [], Topic.includes(:replies).limit(0).pluck(:id)
+ assert_equal [], Topic.includes(:replies).limit(1).where('0 = 1').pluck(:id)
+ end
+
def test_pluck_not_auto_table_name_prefix_if_column_included
Company.create!(:name => "test", :contracts => [Contract.new(:developer_id => 7)])
ids = Company.includes(:contracts).pluck(:developer_id)
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index b64ff13d29..e746ca2805 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -278,8 +278,9 @@ class RelationTest < ActiveRecord::TestCase
def test_null_relation_calculations_methods
assert_no_queries do
- assert_equal 0, Developer.none.count
- assert_equal nil, Developer.none.calculate(:average, 'salary')
+ assert_equal 0, Developer.none.count
+ assert_equal 0, Developer.none.calculate(:count, nil, {})
+ assert_equal nil, Developer.none.calculate(:average, 'salary')
end
end
diff --git a/guides/source/contributing_to_ruby_on_rails.md b/guides/source/contributing_to_ruby_on_rails.md
index 0be9bb1ced..0bfa646b81 100644
--- a/guides/source/contributing_to_ruby_on_rails.md
+++ b/guides/source/contributing_to_ruby_on_rails.md
@@ -182,9 +182,9 @@ Contributing to the Rails Documentation
Ruby on Rails has two main sets of documentation: the guides help you in learning about Ruby on Rails, and the API is a reference.
-You can help improve the Rails guides by making them more coherent, consistent or readable, adding missing information, correcting factual errors, fixing typos, or bringing it up to date with the latest edge Rails. To get involved in the translation of Rails guides, please see [Translating Rails Guides](https://wiki.github.com/lifo/docrails/translating-rails-guides).
+You can help improve the Rails guides by making them more coherent, consistent or readable, adding missing information, correcting factual errors, fixing typos, or bringing it up to date with the latest edge Rails. To get involved in the translation of Rails guides, please see [Translating Rails Guides](https://wiki.github.com/rails/docrails/translating-rails-guides).
-If you're confident about your changes, you can push them directly yourself via [docrails](https://github.com/lifo/docrails). Docrails is a branch with an **open commit policy** and public write access. Commits to docrails are still reviewed, but this happens after they are pushed. Docrails is merged with master regularly, so you are effectively editing the Ruby on Rails documentation.
+If you're confident about your changes, you can push them directly yourself via [docrails](https://github.com/rails/docrails). Docrails is a branch with an **open commit policy** and public write access. Commits to docrails are still reviewed, but this happens after they are pushed. Docrails is merged with master regularly, so you are effectively editing the Ruby on Rails documentation.
If you are unsure of the documentation changes, you can create an issue in the [Rails](https://github.com/rails/rails/issues) issues tracker on GitHub.
diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md
index 599e47949d..1b30f4b728 100644
--- a/guides/source/getting_started.md
+++ b/guides/source/getting_started.md
@@ -64,7 +64,7 @@ Creating a New Rails Project
The best way to use this guide is to follow each step as it happens, no code or
step needed to make this example application has been left out, so you can
literally follow along step by step. You can get the complete code
-[here](https://github.com/lifo/docrails/tree/master/guides/code/getting_started).
+[here](https://github.com/rails/docrails/tree/master/guides/code/getting_started).
By following along with this guide, you'll create a Rails project called
`blog`, a
diff --git a/guides/source/layout.html.erb b/guides/source/layout.html.erb
index 397dd62638..ef2bdf0753 100644
--- a/guides/source/layout.html.erb
+++ b/guides/source/layout.html.erb
@@ -102,10 +102,10 @@
</p>
<p>
If you see any typos or factual errors you are confident to
- patch, please clone <%= link_to 'docrails', 'https://github.com/lifo/docrails' %>
+ patch, please clone <%= link_to 'docrails', 'https://github.com/rails/docrails' %>
and push the change yourself. That branch of Rails has public write access.
Commits are still reviewed, but that happens after you've submitted your
- contribution. <%= link_to 'docrails', 'https://github.com/lifo/docrails' %> is
+ contribution. <%= link_to 'docrails', 'https://github.com/rails/docrails' %> is
cross-merged with master periodically.
</p>
<p>
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index d11b0b7e85..23a7cf6ca3 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,3 +1,7 @@
+* Clearing autoloaded constants triggers routes reloading [Fixes #10685].
+
+ *Xavier Noria*
+
* Fixes bug with scaffold generator with `--assets=false --resource-route=false`.
Fixes #9525.
diff --git a/railties/lib/rails/application/finisher.rb b/railties/lib/rails/application/finisher.rb
index 3ae60312c5..7a1bb1e25c 100644
--- a/railties/lib/rails/application/finisher.rb
+++ b/railties/lib/rails/application/finisher.rb
@@ -62,17 +62,28 @@ module Rails
ActiveSupport.run_load_hooks(:after_initialize, self)
end
- # Set app reload just after the finisher hook to ensure
- # routes added in the hook are still loaded.
+ # Set routes reload after the finisher hook to ensure routes added in
+ # the hook are taken into account.
initializer :set_routes_reloader_hook do
reloader = routes_reloader
reloader.execute_if_updated
self.reloaders << reloader
- ActionDispatch::Reloader.to_prepare { reloader.execute_if_updated }
+ ActionDispatch::Reloader.to_prepare do
+ # We configure #execute rather than #execute_if_updated because if
+ # autoloaded constants are cleared we need to reload routes also in
+ # case any was used there, as in
+ #
+ # mount MailPreview => 'mail_view'
+ #
+ # This means routes are also reloaded if i18n is updated, which
+ # might not be necessary, but in order to be more precise we need
+ # some sort of reloaders dependency support, to be added.
+ reloader.execute
+ end
end
- # Set app reload just after the finisher hook to ensure
- # paths added in the hook are still loaded.
+ # Set clearing dependencies after the finisher hook to ensure paths
+ # added in the hook are taken into account.
initializer :set_clear_dependencies_hook, group: :all do
callback = lambda do
ActiveSupport::DescendantsTracker.clear
@@ -82,9 +93,17 @@ module Rails
if config.reload_classes_only_on_change
reloader = config.file_watcher.new(*watchable_args, &callback)
self.reloaders << reloader
- # We need to set a to_prepare callback regardless of the reloader result, i.e.
- # models should be reloaded if any of the reloaders (i18n, routes) were updated.
- ActionDispatch::Reloader.to_prepare(prepend: true){ reloader.execute }
+
+ # Prepend this callback to have autoloaded constants cleared before
+ # any other possible reloading, in case they need to autoload fresh
+ # constants.
+ ActionDispatch::Reloader.to_prepare(prepend: true) do
+ # In addition to changes detected by the file watcher, if routes
+ # or i18n have been updated we also need to clear constants,
+ # that's why we run #execute rather than #execute_if_updated, this
+ # callback has to clear autoloaded constants after any update.
+ reloader.execute
+ end
else
ActionDispatch::Reloader.to_cleanup(&callback)
end
diff --git a/railties/test/application/loading_test.rb b/railties/test/application/loading_test.rb
index 96aa35b678..17926ac444 100644
--- a/railties/test/application/loading_test.rb
+++ b/railties/test/application/loading_test.rb
@@ -179,7 +179,7 @@ class LoadingTest < ActiveSupport::TestCase
RUBY
app_file 'config/routes.rb', <<-RUBY
- $counter = 0
+ $counter ||= 0
Rails.application.routes.draw do
get '/c', to: lambda { |env| User; [200, {"Content-Type" => "text/plain"}, [$counter.to_s]] }
end
@@ -205,6 +205,39 @@ class LoadingTest < ActiveSupport::TestCase
assert_equal "2", last_response.body
end
+ test "dependencies reloading is followed by routes reloading" do
+ add_to_config <<-RUBY
+ config.cache_classes = false
+ RUBY
+
+ app_file 'config/routes.rb', <<-RUBY
+ $counter ||= 1
+ $counter *= 2
+ AppTemplate::Application.routes.draw do
+ get '/c', to: lambda { |env| User; [200, {"Content-Type" => "text/plain"}, [$counter.to_s]] }
+ end
+ RUBY
+
+ app_file "app/models/user.rb", <<-MODEL
+ class User
+ $counter += 1
+ end
+ MODEL
+
+ require 'rack/test'
+ extend Rack::Test::Methods
+
+ require "#{rails_root}/config/environment"
+
+ get "/c"
+ assert_equal "3", last_response.body
+
+ app_file "db/schema.rb", ""
+
+ get "/c"
+ assert_equal "7", last_response.body
+ end
+
test "columns migrations also trigger reloading" do
add_to_config <<-RUBY
config.cache_classes = false