diff options
-rw-r--r-- | CONTRIBUTING.md | 2 | ||||
-rw-r--r-- | activemodel/lib/active_model/serializers/json.rb | 2 | ||||
-rw-r--r-- | activerecord/lib/active_record/errors.rb | 4 | ||||
-rw-r--r-- | activerecord/lib/active_record/null_relation.rb | 8 | ||||
-rw-r--r-- | activerecord/lib/active_record/relation/calculations.rb | 2 | ||||
-rw-r--r-- | activerecord/lib/active_record/relation/finder_methods.rb | 28 | ||||
-rw-r--r-- | activerecord/test/cases/calculations_test.rb | 11 | ||||
-rw-r--r-- | activerecord/test/cases/relations_test.rb | 5 | ||||
-rw-r--r-- | guides/source/contributing_to_ruby_on_rails.md | 4 | ||||
-rw-r--r-- | guides/source/getting_started.md | 2 | ||||
-rw-r--r-- | guides/source/layout.html.erb | 4 | ||||
-rw-r--r-- | railties/CHANGELOG.md | 4 | ||||
-rw-r--r-- | railties/lib/rails/application/finisher.rb | 35 | ||||
-rw-r--r-- | railties/test/application/loading_test.rb | 35 |
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 |