diff options
152 files changed, 1195 insertions, 698 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: @@ -27,7 +27,13 @@ gem 'sass-rails', github: 'rails/sass-rails', branch: 'master' # require: false so bcrypt is loaded only when has_secure_password is used. # This is to avoid Active Model (and by extension the entire framework) # being dependent on a binary library. -gem 'bcrypt', '~> 3.1.10', require: false +platforms :mingw, :x64_mingw, :mswin, :mswin64 do + gem 'bcrypt-ruby', '~> 3.0.0', require: false +end + +platforms :ruby, :jruby, :rbx do + gem 'bcrypt', '~> 3.1.10', require: false +end # This needs to be with require false to avoid it being automatically loaded by # sprockets. @@ -45,7 +51,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 @@ -80,7 +86,7 @@ group :test do gem 'benchmark-ips' end -platforms :ruby do +platforms :ruby, :mswin, :mswin64, :mingw, :x64_mingw do gem 'nokogiri', '>= 1.6.7' # Needed for compiling the ActionDispatch::Journey parser. diff --git a/Gemfile.lock b/Gemfile.lock index d7abc12b21..b80cef0efc 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -168,9 +168,8 @@ GEM backburner (1.1.0) beaneater (~> 1.0) dante (> 0.1.5) - bcrypt (3.1.10) - bcrypt (3.1.10-x64-mingw32) - bcrypt (3.1.10-x86-mingw32) + bcrypt-ruby (3.0.1) + bcrypt-ruby (3.0.1-x86-mingw32) beaneater (1.0.0) benchmark-ips (2.3.0) builder (3.2.2) @@ -238,10 +237,16 @@ GEM multi_json (1.11.2) mustache (1.0.2) mysql (2.9.1) - mysql2 (0.4.1) + mysql2 (0.4.2-x64-mingw32) nokogiri (1.6.7) mini_portile2 (~> 2.0.0.rc2) + nokogiri (1.6.7-x64-mingw32) + mini_portile2 (~> 2.0.0.rc2) + nokogiri (1.6.7-x86-mingw32) + mini_portile2 (~> 2.0.0.rc2) pg (0.18.3) + pg (0.18.3-x64-mingw32) + pg (0.18.3-x86-mingw32) psych (2.0.15) que (0.11.2) racc (1.4.13) @@ -299,6 +304,8 @@ GEM thor thread (~> 0.1.7) sqlite3 (1.3.11) + sqlite3 (1.3.11-x64-mingw32) + sqlite3 (1.3.11-x86-mingw32) stackprof (0.2.7) sucker_punch (1.6.0) celluloid (~> 0.17.2) @@ -332,6 +339,7 @@ DEPENDENCIES arel! backburner bcrypt (~> 3.1.10) + bcrypt-ruby (~> 3.0.0) benchmark-ips byebug coffee-rails (~> 4.1.0) 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/abstract_controller/callbacks.rb b/actionpack/lib/abstract_controller/callbacks.rb index d5317e4717..d63ce9c1c3 100644 --- a/actionpack/lib/abstract_controller/callbacks.rb +++ b/actionpack/lib/abstract_controller/callbacks.rb @@ -48,7 +48,8 @@ module AbstractController def _normalize_callback_option(options, from, to) # :nodoc: if from = options[from] - from = Array(from).map {|o| "action_name == '#{o}'"}.join(" || ") + _from = Array(from).map(&:to_s).to_set + from = proc {|c| _from.include? c.action_name } options[to] = Array(options[to]).unshift(from) end end diff --git a/actionpack/lib/action_controller/caching/fragments.rb b/actionpack/lib/action_controller/caching/fragments.rb index 2694d4c12f..b9ad51a9cf 100644 --- a/actionpack/lib/action_controller/caching/fragments.rb +++ b/actionpack/lib/action_controller/caching/fragments.rb @@ -14,12 +14,57 @@ module ActionController # # expire_fragment('name_of_cache') module Fragments + extend ActiveSupport::Concern + + included do + if respond_to?(:class_attribute) + class_attribute :fragment_cache_keys + else + mattr_writer :fragment_cache_keys + end + + self.fragment_cache_keys = [] + + helper_method :fragment_cache_key if respond_to?(:helper_method) + end + + module ClassMethods + # Allows you to specify controller-wide key prefixes for + # cache fragments. Pass either a constant +value+, or a block + # which computes a value each time a cache key is generated. + # + # For example, you may want to prefix all fragment cache keys + # with a global version identifier, so you can easily + # invalidate all caches. + # + # class ApplicationController + # fragment_cache_key "v1" + # end + # + # When it's time to invalidate all fragments, simply change + # the string constant. Or, progressively roll out the cache + # invalidation using a computed value: + # + # class ApplicationController + # fragment_cache_key do + # @account.id.odd? ? "v1" : "v2" + # end + # end + def fragment_cache_key(value = nil, &key) + self.fragment_cache_keys += [key || ->{ value }] + end + end + # Given a key (as described in +expire_fragment+), returns # a key suitable for use in reading, writing, or expiring a - # cached fragment. All keys are prefixed with <tt>views/</tt> and uses - # ActiveSupport::Cache.expand_cache_key for the expansion. + # cached fragment. All keys begin with <tt>views/</tt>, + # followed by any controller-wide key prefix values, ending + # with the specified +key+ value. The key is expanded using + # ActiveSupport::Cache.expand_cache_key. def fragment_cache_key(key) - ActiveSupport::Cache.expand_cache_key(key.is_a?(Hash) ? url_for(key).split("://").last : key, :views) + head = self.class.fragment_cache_keys.map { |k| instance_exec(&k) } + tail = key.is_a?(Hash) ? url_for(key).split("://").last : key + ActiveSupport::Cache.expand_cache_key([*head, *tail], :views) end # Writes +content+ to the location signified by diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb index 0a36fecd27..2ac6e37e34 100644 --- a/actionpack/lib/action_controller/metal/http_authentication.rb +++ b/actionpack/lib/action_controller/metal/http_authentication.rb @@ -397,7 +397,7 @@ module ActionController # RewriteRule ^(.*)$ dispatch.fcgi [E=X-HTTP_AUTHORIZATION:%{HTTP:Authorization},QSA,L] module Token TOKEN_KEY = 'token=' - TOKEN_REGEX = /^(Token|Bearer) / + TOKEN_REGEX = /^(Token|Bearer)\s+/ AUTHN_PAIR_DELIMITERS = /(?:,|;|\t+)/ extend self 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/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index c4228df925..2bd2e53252 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -30,9 +30,9 @@ module ActionDispatch controller = controller req res = controller.make_response! req dispatch(controller, params[:action], req, res) - rescue NameError => e + rescue ActionController::RoutingError if @raise_on_name_error - raise ActionController::RoutingError, e.message, e.backtrace + raise else return [404, {'X-Cascade' => 'pass'}, []] end @@ -42,6 +42,8 @@ module ActionDispatch def controller(req) req.controller_class + rescue NameError => e + raise ActionController::RoutingError, e.message, e.backtrace end def dispatch(controller, action, req, res) diff --git a/actionpack/lib/action_dispatch/testing/assertions/response.rb b/actionpack/lib/action_dispatch/testing/assertions/response.rb index 5af052afb4..c138660a21 100644 --- a/actionpack/lib/action_dispatch/testing/assertions/response.rb +++ b/actionpack/lib/action_dispatch/testing/assertions/response.rb @@ -31,7 +31,7 @@ module ActionDispatch if Symbol === type if [:success, :missing, :redirect, :error].include?(type) - assert_predicate @response, RESPONSE_PREDICATES[type], message + assert @response.send(RESPONSE_PREDICATES[type]), message else code = Rack::Utils::SYMBOL_TO_STATUS_CODE[type] if code.nil? @@ -85,17 +85,15 @@ module ActionDispatch end end - def generate_response_message(type) - message = "Expected response to be a <#{type}>, but was" - - if @response.redirection? - redirect_is = normalize_argument_to_redirection(@response.location) - message << " a redirect to <#{redirect_is}>" - else - message << " <#{@response.response_code}>" - end + def generate_response_message(type, code = @response.response_code) + "Expected response to be a <#{type}>, but was a <#{code}>" + .concat location_if_redirected + end - message + def location_if_redirected + return '' unless @response.redirection? && @response.location.present? + location = normalize_argument_to_redirection(@response.location) + " redirect to <#{location}>" end end 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/assertions/response_assertions_test.rb b/actionpack/test/assertions/response_assertions_test.rb index 6c7036aa1a..e76c222824 100644 --- a/actionpack/test/assertions/response_assertions_test.rb +++ b/actionpack/test/assertions/response_assertions_test.rb @@ -64,13 +64,34 @@ module ActionDispatch } end - def test_message_when_response_is_redirect_but_asserted_for_status_other_than_redirect - @response = FakeResponse.new :redirection, "http://test.host/posts/redirect/1" - error = assert_raises(Minitest::Assertion) do - assert_response :success - end + def test_error_message_shows_404_when_404_asserted_for_success + @response = ActionDispatch::Response.new + @response.status = 404 + + error = assert_raises(Minitest::Assertion) { assert_response :success } + expected = "Expected response to be a <success>, but was a <404>" + assert_match expected, error.message + end + + def test_error_message_shows_302_redirect_when_302_asserted_for_success + @response = ActionDispatch::Response.new + @response.status = 302 + @response.location = 'http://test.host/posts/redirect/1' + + error = assert_raises(Minitest::Assertion) { assert_response :success } + expected = "Expected response to be a <success>, but was a <302>" \ + " redirect to <http://test.host/posts/redirect/1>" + assert_match expected, error.message + end + + def test_error_message_shows_302_redirect_when_302_asserted_for_301 + @response = ActionDispatch::Response.new + @response.status = 302 + @response.location = 'http://test.host/posts/redirect/2' - expected = "Expected response to be a <success>, but was a redirect to <http://test.host/posts/redirect/1>." + error = assert_raises(Minitest::Assertion) { assert_response 301 } + expected = "Expected response to be a <301>, but was a <302>" \ + " redirect to <http://test.host/posts/redirect/2>" assert_match expected, error.message end end diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb index bc0ffd3eaa..d19b3810c2 100644 --- a/actionpack/test/controller/caching_test.rb +++ b/actionpack/test/controller/caching_test.rb @@ -419,3 +419,28 @@ class AutomaticCollectionCacheTest < ActionController::TestCase assert_equal 1, @controller.partial_rendered_times end end + +class FragmentCacheKeyTestController < CachingController + attr_accessor :account_id + + fragment_cache_key "v1" + fragment_cache_key { account_id } +end + +class FragmentCacheKeyTest < ActionController::TestCase + def setup + super + @store = ActiveSupport::Cache::MemoryStore.new + @controller = FragmentCacheKeyTestController.new + @controller.perform_caching = true + @controller.cache_store = @store + end + + def test_fragment_cache_key + @controller.account_id = "123" + assert_equal 'views/v1/123/what a key', @controller.fragment_cache_key('what a key') + + @controller.account_id = nil + assert_equal 'views/v1//what a key', @controller.fragment_cache_key('what a key') + end +end diff --git a/actionpack/test/controller/helper_test.rb b/actionpack/test/controller/helper_test.rb index 3ecfedefd1..feb882a2b3 100644 --- a/actionpack/test/controller/helper_test.rb +++ b/actionpack/test/controller/helper_test.rb @@ -141,20 +141,10 @@ class HelperTest < ActiveSupport::TestCase def test_helper_for_nested_controller assert_equal 'hello: Iz guuut!', call_controller(Fun::GamesController, "render_hello_world").last.body - # request = ActionController::TestRequest.new - # - # resp = Fun::GamesController.action(:render_hello_world).call(request.env) - # assert_equal 'hello: Iz guuut!', resp.last.body end def test_helper_for_acronym_controller assert_equal "test: baz", call_controller(Fun::PdfController, "test").last.body - # - # request = ActionController::TestRequest.new - # response = ActionDispatch::TestResponse.new - # request.action = 'test' - # - # assert_equal 'test: baz', Fun::PdfController.process(request, response).body end def test_default_helpers_only diff --git a/actionpack/test/controller/http_token_authentication_test.rb b/actionpack/test/controller/http_token_authentication_test.rb index 9c5a01c318..98e3c891a7 100644 --- a/actionpack/test/controller/http_token_authentication_test.rb +++ b/actionpack/test/controller/http_token_authentication_test.rb @@ -94,6 +94,14 @@ class HttpTokenAuthenticationTest < ActionController::TestCase assert_response :success end + test "authentication request with tab in header" do + @request.env['HTTP_AUTHORIZATION'] = "Token\ttoken=\"lifo\"" + get :index + + assert_response :success + assert_equal 'Hello Secret', @response.body + end + test "authentication request without credential" do get :display 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/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 8972f3e74d..82222a141c 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -4592,3 +4592,44 @@ class TestDefaultUrlOptions < ActionDispatch::IntegrationTest assert_equal '/en/posts/2014/12/13', archived_posts_path(2014, 12, 13) end end + +class TestErrorsInController < ActionDispatch::IntegrationTest + class ::PostsController < ActionController::Base + def foo + nil.i_do_not_exist + end + + def bar + NonExistingClass.new + end + end + + Routes = ActionDispatch::Routing::RouteSet.new + Routes.draw do + get '/:controller(/:action)' + end + + APP = build_app Routes + + def app + APP + end + + def test_legit_no_method_errors_are_not_caught + get '/posts/foo' + assert_equal 500, response.status + end + + def test_legit_name_errors_are_not_caught + get '/posts/bar' + assert_equal 500, response.status + end + + def test_legit_routing_not_found_responses + get '/posts/baz' + assert_equal 404, response.status + + get '/i_do_not_exist' + assert_equal 404, response.status + end +end diff --git a/actionview/lib/action_view/helpers/cache_helper.rb b/actionview/lib/action_view/helpers/cache_helper.rb index e473aeaea9..18b2102d73 100644 --- a/actionview/lib/action_view/helpers/cache_helper.rb +++ b/actionview/lib/action_view/helpers/cache_helper.rb @@ -216,14 +216,6 @@ module ActionView end end - # Given a key (as described in ActionController::Caching::Fragments.expire_fragment), - # returns a key suitable for use in reading, writing, or expiring a - # cached fragment. All keys are prefixed with <tt>views/</tt> and uses - # ActiveSupport::Cache.expand_cache_key for the expansion. - def fragment_cache_key(key) - ActiveSupport::Cache.expand_cache_key(key.is_a?(Hash) ? url_for(key).split("://").last : key, :views) - end - private def fragment_name_with_digest(name, virtual_path) #:nodoc: diff --git a/actionview/test/template/date_helper_test.rb b/actionview/test/template/date_helper_test.rb index c4234a71c3..92e77599f4 100644 --- a/actionview/test/template/date_helper_test.rb +++ b/actionview/test/template/date_helper_test.rb @@ -390,11 +390,6 @@ class DateHelperTest < ActionView::TestCase expected << "</select>\n" assert_dom_equal expected, select_month(Time.mktime(2003, 8, 16), {}, :class => 'selector', :accesskey => 'M') - #result = select_month(Time.mktime(2003, 8, 16), {}, :class => 'selector', :accesskey => 'M') - #assert result.include?('<select id="date_month" name="date[month]"') - #assert result.include?('class="selector"') - #assert result.include?('accesskey="M"') - #assert result.include?('<option value="1">January') end def test_select_month_with_default_prompt @@ -474,11 +469,6 @@ class DateHelperTest < ActionView::TestCase expected << "</select>\n" assert_dom_equal expected, select_year(Time.mktime(2003, 8, 16), {:start_year => 2003, :end_year => 2005}, :class => 'selector', :accesskey => 'M') - #result = select_year(Time.mktime(2003, 8, 16), {:start_year => 2003, :end_year => 2005}, :class => 'selector', :accesskey => 'M') - #assert result.include?('<select id="date_year" name="date[year]"') - #assert result.include?('class="selector"') - #assert result.include?('accesskey="M"') - #assert result.include?('<option value="2003"') end def test_select_year_with_default_prompt @@ -639,12 +629,6 @@ class DateHelperTest < ActionView::TestCase expected << "</select>\n" assert_dom_equal expected, select_minute(Time.mktime(2003, 8, 16, 8, 4, 18), {}, :class => 'selector', :accesskey => 'M') - - #result = select_minute(Time.mktime(2003, 8, 16, 8, 4, 18), {}, :class => 'selector', :accesskey => 'M') - #assert result.include?('<select id="date_minute" name="date[minute]"') - #assert result.include?('class="selector"') - #assert result.include?('accesskey="M"') - #assert result.include?('<option value="00">00') end def test_select_minute_with_default_prompt @@ -709,12 +693,6 @@ class DateHelperTest < ActionView::TestCase expected << "</select>\n" assert_dom_equal expected, select_second(Time.mktime(2003, 8, 16, 8, 4, 18), {}, :class => 'selector', :accesskey => 'M') - - #result = select_second(Time.mktime(2003, 8, 16, 8, 4, 18), {}, :class => 'selector', :accesskey => 'M') - #assert result.include?('<select id="date_second" name="date[second]"') - #assert result.include?('class="selector"') - #assert result.include?('accesskey="M"') - #assert result.include?('<option value="00">00') end def test_select_second_with_default_prompt diff --git a/actionview/test/template/render_test.rb b/actionview/test/template/render_test.rb index 84aca222b2..994fd44c52 100644 --- a/actionview/test/template/render_test.rb +++ b/actionview/test/template/render_test.rb @@ -9,6 +9,10 @@ module RenderTestCases @assigns = { :secret => 'in the sauce' } @view = Class.new(ActionView::Base) do def view_cache_dependencies; end + + def fragment_cache_key(key) + ActiveSupport::Cache.expand_cache_key(key, :views) + end end.new(paths, @assigns) @controller_view = TestController.new.view_context 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/callbacks_test.rb b/activemodel/test/cases/callbacks_test.rb index 85455c112c..e4ecc0adb4 100644 --- a/activemodel/test/cases/callbacks_test.rb +++ b/activemodel/test/cases/callbacks_test.rb @@ -28,7 +28,7 @@ class CallbacksTest < ActiveModel::TestCase false end - after_create "@callbacks << :final_callback" + ActiveSupport::Deprecation.silence { after_create "@callbacks << :final_callback" } def initialize(options = {}) @callbacks = [] 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/activemodel/test/cases/validations/with_validation_test.rb b/activemodel/test/cases/validations/with_validation_test.rb index 03c7943308..c73580138d 100644 --- a/activemodel/test/cases/validations/with_validation_test.rb +++ b/activemodel/test/cases/validations/with_validation_test.rb @@ -101,6 +101,7 @@ class ValidatesWithTest < ActiveModel::TestCase validator.expect(:new, validator, [{foo: :bar, if: "1 == 1", class: Topic}]) validator.expect(:validate, nil, [topic]) validator.expect(:is_a?, false, [Symbol]) + validator.expect(:is_a?, false, [String]) Topic.validates_with(validator, if: "1 == 1", foo: :bar) assert topic.valid? diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 56a3232ee9..f1921dded8 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,33 @@ +* Introduce ApplicationRecord, an Active Record layer super type. + + An `ApplicationRecord` let's engines have models, isolated from the main + application. Plugin authors can use it to distribute extensions as modules + to be included into `ApplicationRecord`, instead of monkey patches. It can + also serve as a place for applications to customize the default + `ActiveRecord::Base` model behaviour. + + Newly generated applications have `app/models/application_record.rb` + present by default. Generators are smart enough to recognize that + newly generated models have to inherit from `ApplicationRecord` only if + it's present. + + *Genadi Samokovarov* + +* Version the API presented to migration classes, so we can change parameter + defaults without breaking existing migrations, or forcing them to be + rewritten through a deprecation cycle. + + *Matthew Draper*, *Ravil Bayramgalin* + +* 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/README.rdoc b/activerecord/README.rdoc index 7eb4e9db1a..20ce1e8dd2 100644 --- a/activerecord/README.rdoc +++ b/activerecord/README.rdoc @@ -138,7 +138,7 @@ This would also define the following accessors: <tt>Product#name</tt> and * Database agnostic schema management with Migrations. - class AddSystemSettings < ActiveRecord::Migration + class AddSystemSettings < ActiveRecord::Migration[5.0] def up create_table :system_settings do |t| t.string :name diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index b806a2f832..04ad45f5da 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1639,7 +1639,7 @@ module ActiveRecord # The join table should not have a primary key or a model associated with it. You must manually generate the # join table with a migration such as this: # - # class CreateDevelopersProjectsJoinTable < ActiveRecord::Migration + # class CreateDevelopersProjectsJoinTable < ActiveRecord::Migration[5.0] # def change # create_join_table :developers, :projects # end diff --git a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb index 9e693b6aee..45d2c855a5 100644 --- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb +++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/string/strip' + module ActiveRecord module AttributeMethods module TimeZoneConversion @@ -77,7 +79,7 @@ module ActiveRecord !result && cast_type.type == :time && time_zone_aware_types.include?(:not_explicitly_configured) - ActiveSupport::Deprecation.warn(<<-MESSAGE) + ActiveSupport::Deprecation.warn(<<-MESSAGE.strip_heredoc) Time columns will become time zone aware in Rails 5.1. This still causes `String`s to be parsed as if they were in `Time.zone`, and `Time`s to be converted to `Time.zone`. diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb index 4058affec3..854f9776a3 100644 --- a/activerecord/lib/active_record/callbacks.rb +++ b/activerecord/lib/active_record/callbacks.rb @@ -175,21 +175,6 @@ module ActiveRecord # end # end # - # The callback macros usually accept a symbol for the method they're supposed to run, but you can also - # pass a "method string", which will then be evaluated within the binding of the callback. Example: - # - # class Topic < ActiveRecord::Base - # before_destroy 'self.class.delete_all "parent_id = #{id}"' - # end - # - # Notice that single quotes (') are used so the <tt>#{id}</tt> part isn't evaluated until the callback - # is triggered. Also note that these inline callbacks can be stacked just like the regular ones: - # - # class Topic < ActiveRecord::Base - # before_destroy 'self.class.delete_all "parent_id = #{id}"', - # 'puts "Evaluated after parents are destroyed"' - # end - # # == <tt>before_validation*</tt> returning statements # # If the +before_validation+ callback throws +:abort+, the process will be 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 159cbcb85a..1cda23dc1d 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -190,7 +190,7 @@ module ActiveRecord # Inside migration files, the +t+ object in {create_table}[rdoc-ref:SchemaStatements#create_table] # is actually of this type: # - # class SomeMigration < ActiveRecord::Migration + # class SomeMigration < ActiveRecord::Migration[5.0] # def up # create_table :foo do |t| # puts t.class # => "ActiveRecord::ConnectionAdapters::TableDefinition" diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb index 7ca597859d..6590e0140d 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb @@ -10,9 +10,9 @@ module ActiveRecord config = config.symbolize_keys config[:username] = 'root' if config[:username].nil? - + config[:flags] ||= 0 if Mysql2::Client.const_defined? :FOUND_ROWS - config[:flags] = Mysql2::Client::FOUND_ROWS + config[:flags] |= Mysql2::Client::FOUND_ROWS end client = Mysql2::Client.new(config) 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/migration.rb b/activerecord/lib/active_record/migration.rb index ca2537cdc3..43726d795e 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -13,7 +13,7 @@ module ActiveRecord # For example the following migration is not reversible. # Rolling back this migration will raise an ActiveRecord::IrreversibleMigration error. # - # class IrreversibleMigrationExample < ActiveRecord::Migration + # class IrreversibleMigrationExample < ActiveRecord::Migration[5.0] # def change # create_table :distributors do |t| # t.string :zipcode @@ -31,7 +31,7 @@ module ActiveRecord # # 1. Define <tt>#up</tt> and <tt>#down</tt> methods instead of <tt>#change</tt>: # - # class ReversibleMigrationExample < ActiveRecord::Migration + # class ReversibleMigrationExample < ActiveRecord::Migration[5.0] # def up # create_table :distributors do |t| # t.string :zipcode @@ -56,7 +56,7 @@ module ActiveRecord # # 2. Use the #reversible method in <tt>#change</tt> method: # - # class ReversibleMigrationExample < ActiveRecord::Migration + # class ReversibleMigrationExample < ActiveRecord::Migration[5.0] # def change # create_table :distributors do |t| # t.string :zipcode @@ -155,7 +155,7 @@ module ActiveRecord # # Example of a simple migration: # - # class AddSsl < ActiveRecord::Migration + # class AddSsl < ActiveRecord::Migration[5.0] # def up # add_column :accounts, :ssl_enabled, :boolean, default: true # end @@ -175,7 +175,7 @@ module ActiveRecord # # Example of a more complex migration that also needs to initialize data: # - # class AddSystemSettings < ActiveRecord::Migration + # class AddSystemSettings < ActiveRecord::Migration[5.0] # def up # create_table :system_settings do |t| # t.string :name @@ -301,7 +301,7 @@ module ActiveRecord # rails generate migration add_fieldname_to_tablename fieldname:string # # This will generate the file <tt>timestamp_add_fieldname_to_tablename.rb</tt>, which will look like this: - # class AddFieldnameToTablename < ActiveRecord::Migration + # class AddFieldnameToTablename < ActiveRecord::Migration[5.0] # def change # add_column :tablenames, :fieldname, :string # end @@ -332,7 +332,7 @@ module ActiveRecord # # Not all migrations change the schema. Some just fix the data: # - # class RemoveEmptyTags < ActiveRecord::Migration + # class RemoveEmptyTags < ActiveRecord::Migration[5.0] # def up # Tag.all.each { |tag| tag.destroy if tag.pages.empty? } # end @@ -345,7 +345,7 @@ module ActiveRecord # # Others remove columns when they migrate up instead of down: # - # class RemoveUnnecessaryItemAttributes < ActiveRecord::Migration + # class RemoveUnnecessaryItemAttributes < ActiveRecord::Migration[5.0] # def up # remove_column :items, :incomplete_items_count # remove_column :items, :completed_items_count @@ -359,7 +359,7 @@ module ActiveRecord # # And sometimes you need to do something in SQL not abstracted directly by migrations: # - # class MakeJoinUnique < ActiveRecord::Migration + # class MakeJoinUnique < ActiveRecord::Migration[5.0] # def up # execute "ALTER TABLE `pages_linked_pages` ADD UNIQUE `page_id_linked_page_id` (`page_id`,`linked_page_id`)" # end @@ -376,7 +376,7 @@ module ActiveRecord # <tt>Base#reset_column_information</tt> in order to ensure that the model has the # latest column data from after the new column was added. Example: # - # class AddPeopleSalary < ActiveRecord::Migration + # class AddPeopleSalary < ActiveRecord::Migration[5.0] # def up # add_column :people, :salary, :integer # Person.reset_column_information @@ -434,7 +434,7 @@ module ActiveRecord # To define a reversible migration, define the +change+ method in your # migration like this: # - # class TenderloveMigration < ActiveRecord::Migration + # class TenderloveMigration < ActiveRecord::Migration[5.0] # def change # create_table(:horses) do |t| # t.column :content, :text @@ -464,7 +464,7 @@ module ActiveRecord # can't execute inside a transaction though, and for these situations # you can turn the automatic transactions off. # - # class ChangeEnum < ActiveRecord::Migration + # class ChangeEnum < ActiveRecord::Migration[5.0] # disable_ddl_transaction! # # def up @@ -476,6 +476,32 @@ module ActiveRecord # are in a Migration with <tt>self.disable_ddl_transaction!</tt>. class Migration autoload :CommandRecorder, 'active_record/migration/command_recorder' + autoload :Compatibility, 'active_record/migration/compatibility' + + # This must be defined before the inherited hook, below + class Current < Migration # :nodoc: + end + + def self.inherited(subclass) # :nodoc: + super + if subclass.superclass == Migration + subclass.include Compatibility::Legacy + end + end + + def self.[](version) + version = version.to_s + name = "V#{version.tr('.', '_')}" + unless Compatibility.const_defined?(name) + versions = Compatibility.constants.grep(/\AV[0-9_]+\z/).map { |s| s.to_s.delete('V').tr('_', '.').inspect } + raise "Unknown migration version #{version.inspect}; expected one of #{versions.sort.join(', ')}" + end + Compatibility.const_get(name) + end + + def self.current_version + Rails.version.to_f + end MigrationFilenameRegexp = /\A([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/ # :nodoc: @@ -509,6 +535,10 @@ module ActiveRecord attr_accessor :delegate # :nodoc: attr_accessor :disable_ddl_transaction # :nodoc: + def nearest_delegate # :nodoc: + delegate || superclass.nearest_delegate + end + # Raises <tt>ActiveRecord::PendingMigrationError</tt> error if any migrations are pending. def check_pending!(connection = Base.connection) raise ActiveRecord::PendingMigrationError if ActiveRecord::Migrator.needs_migration?(connection) @@ -535,7 +565,7 @@ module ActiveRecord end def method_missing(name, *args, &block) # :nodoc: - (delegate || superclass.delegate).send(name, *args, &block) + nearest_delegate.send(name, *args, &block) end def migrate(direction) @@ -575,7 +605,7 @@ module ActiveRecord # and create the table 'apples' on the way up, and the reverse # on the way down. # - # class FixTLMigration < ActiveRecord::Migration + # class FixTLMigration < ActiveRecord::Migration[5.0] # def change # revert do # create_table(:horses) do |t| @@ -594,7 +624,7 @@ module ActiveRecord # # require_relative '20121212123456_tenderlove_migration' # - # class FixupTLMigration < ActiveRecord::Migration + # class FixupTLMigration < ActiveRecord::Migration[5.0] # def change # revert TenderloveMigration # @@ -647,7 +677,7 @@ module ActiveRecord # when the three columns 'first_name', 'last_name' and 'full_name' exist, # even when migrating down: # - # class SplitNameMigration < ActiveRecord::Migration + # class SplitNameMigration < ActiveRecord::Migration[5.0] # def change # add_column :users, :first_name, :string # add_column :users, :last_name, :string diff --git a/activerecord/lib/active_record/migration/compatibility.rb b/activerecord/lib/active_record/migration/compatibility.rb new file mode 100644 index 0000000000..4c8db8a2d5 --- /dev/null +++ b/activerecord/lib/active_record/migration/compatibility.rb @@ -0,0 +1,54 @@ +module ActiveRecord + class Migration + module Compatibility # :nodoc: all + V5_0 = Current + + module FourTwoShared + module TableDefinition + def timestamps(*, **options) + options[:null] = true if options[:null].nil? + super + end + end + + def create_table(table_name, options = {}) + if block_given? + super(table_name, options) do |t| + class << t + prepend TableDefinition + end + yield t + end + else + super + end + end + + def add_timestamps(*, **options) + options[:null] = true if options[:null].nil? + super + end + end + + class V4_2 < V5_0 + # 4.2 is defined as a module because it needs to be shared with + # Legacy. When the time comes, V5_0 should be defined straight + # in its class. + include FourTwoShared + end + + module Legacy + include FourTwoShared + + def run(*) + ActiveSupport::Deprecation.warn \ + "Directly inheriting from ActiveRecord::Migration is deprecated. " \ + "Please specify the Rails release the migration was written for:\n" \ + "\n" \ + " class #{self.class.name} < ActiveRecord::Migration[4.2]" + super + end + end + end + end +end diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb index e3f304b0af..5df67cdbe7 100644 --- a/activerecord/lib/active_record/model_schema.rb +++ b/activerecord/lib/active_record/model_schema.rb @@ -275,7 +275,7 @@ module ActiveRecord # when just after creating a table you want to populate it with some default # values, eg: # - # class CreateJobLevels < ActiveRecord::Migration + # class CreateJobLevels < ActiveRecord::Migration[5.0] # def up # create_table :job_levels do |t| # t.integer :id 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..983bf019bc 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -3,6 +3,7 @@ require "active_record/relation/query_attribute" require "active_record/relation/where_clause" require "active_record/relation/where_clause_factory" require 'active_model/forbidden_attributes_protection' +require 'active_support/core_ext/string/filters' module ActiveRecord module QueryMethods @@ -97,7 +98,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 +693,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.squish) + 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 +950,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 +1206,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/lib/active_record/schema.rb b/activerecord/lib/active_record/schema.rb index 31dd584538..fdf9965a82 100644 --- a/activerecord/lib/active_record/schema.rb +++ b/activerecord/lib/active_record/schema.rb @@ -27,7 +27,7 @@ module ActiveRecord # # ActiveRecord::Schema is only supported by database adapters that also # support migrations, the two features being very similar. - class Schema < Migration + class Schema < Migration::Current # Eval the given block. All methods available to the current connection # adapter are available within the block, so you can easily use the # database definition DSL to build up your schema ( diff --git a/activerecord/lib/rails/generators/active_record/migration/templates/create_table_migration.rb b/activerecord/lib/rails/generators/active_record/migration/templates/create_table_migration.rb index fadab2a1e6..5f7201cfe1 100644 --- a/activerecord/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +++ b/activerecord/lib/rails/generators/active_record/migration/templates/create_table_migration.rb @@ -1,4 +1,4 @@ -class <%= migration_class_name %> < ActiveRecord::Migration +class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>] def change create_table :<%= table_name %><%= primary_key_type %> do |t| <% attributes.each do |attribute| -%> diff --git a/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb b/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb index 23a377db6a..107f107dc4 100644 --- a/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb +++ b/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb @@ -1,4 +1,4 @@ -class <%= migration_class_name %> < ActiveRecord::Migration +class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>] <%- if migration_action == 'add' -%> def change <% attributes.each do |attribute| -%> diff --git a/activerecord/lib/rails/generators/active_record/model/model_generator.rb b/activerecord/lib/rails/generators/active_record/model/model_generator.rb index 395951ac9d..e4c9539362 100644 --- a/activerecord/lib/rails/generators/active_record/model/model_generator.rb +++ b/activerecord/lib/rails/generators/active_record/model/model_generator.rb @@ -43,9 +43,16 @@ module ActiveRecord # Used by the migration template to determine the parent name of the model def parent_class_name - options[:parent] || "ActiveRecord::Base" + options[:parent] || determine_default_parent_class end + def determine_default_parent_class + if File.exist?('app/models/application_record.rb') + "ApplicationRecord" + else + "ActiveRecord::Base" + end + end end end end diff --git a/activerecord/test/cases/adapters/mysql2/connection_test.rb b/activerecord/test/cases/adapters/mysql2/connection_test.rb index 507d024bb6..8fabcfb5c0 100644 --- a/activerecord/test/cases/adapters/mysql2/connection_test.rb +++ b/activerecord/test/cases/adapters/mysql2/connection_test.rb @@ -83,6 +83,13 @@ class Mysql2ConnectionTest < ActiveRecord::Mysql2TestCase assert_equal [['']], result.rows end end + + def test_passing_arbitary_flags_to_adapter + run_without_connection do |orig_connection| + ActiveRecord::Base.establish_connection(orig_connection.merge({flags: Mysql2::Client::COMPRESS})) + assert_equal (Mysql2::Client::COMPRESS | Mysql2::Client::FOUND_ROWS), ActiveRecord::Base.connection.raw_connection.query_options[:flags] + end + end def test_mysql_strict_mode_specified_default run_without_connection do |orig_connection| diff --git a/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb b/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb index 9cfc133308..b2a805333c 100644 --- a/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb +++ b/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb @@ -3,13 +3,13 @@ require "cases/helper" class PostgresqlExtensionMigrationTest < ActiveRecord::PostgreSQLTestCase self.use_transactional_tests = false - class EnableHstore < ActiveRecord::Migration + class EnableHstore < ActiveRecord::Migration::Current def change enable_extension "hstore" end end - class DisableHstore < ActiveRecord::Migration + class DisableHstore < ActiveRecord::Migration::Current def change disable_extension "hstore" end diff --git a/activerecord/test/cases/adapters/postgresql/hstore_test.rb b/activerecord/test/cases/adapters/postgresql/hstore_test.rb index 6a2d501646..27cc65a643 100644 --- a/activerecord/test/cases/adapters/postgresql/hstore_test.rb +++ b/activerecord/test/cases/adapters/postgresql/hstore_test.rb @@ -86,7 +86,7 @@ if ActiveRecord::Base.connection.supports_extensions? end def test_hstore_migration - hstore_migration = Class.new(ActiveRecord::Migration) do + hstore_migration = Class.new(ActiveRecord::Migration::Current) do def change change_table("hstores") do |t| t.hstore :keys diff --git a/activerecord/test/cases/adapters/postgresql/uuid_test.rb b/activerecord/test/cases/adapters/postgresql/uuid_test.rb index 7127d69e9e..049ed1732e 100644 --- a/activerecord/test/cases/adapters/postgresql/uuid_test.rb +++ b/activerecord/test/cases/adapters/postgresql/uuid_test.rb @@ -20,6 +20,8 @@ class PostgresqlUUIDTest < ActiveRecord::PostgreSQLTestCase end setup do + enable_extension!('uuid-ossp', connection) + connection.create_table "uuid_data_type" do |t| t.uuid 'guid' end 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/callbacks_test.rb b/activerecord/test/cases/callbacks_test.rb index 73ac30e547..4f70ae3a1d 100644 --- a/activerecord/test/cases/callbacks_test.rb +++ b/activerecord/test/cases/callbacks_test.rb @@ -33,7 +33,7 @@ class CallbackDeveloper < ActiveRecord::Base ActiveRecord::Callbacks::CALLBACKS.each do |callback_method| next if callback_method.to_s =~ /^around_/ define_callback_method(callback_method) - send(callback_method, callback_string(callback_method)) + ActiveSupport::Deprecation.silence { send(callback_method, callback_string(callback_method)) } send(callback_method, callback_proc(callback_method)) send(callback_method, callback_object(callback_method)) send(callback_method) { |model| model.history << [callback_method, :block] } 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/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb index f30ed4fcc8..c73958900b 100644 --- a/activerecord/test/cases/fixtures_test.rb +++ b/activerecord/test/cases/fixtures_test.rb @@ -184,7 +184,6 @@ class FixturesTest < ActiveRecord::TestCase end def test_fixtures_from_root_yml_with_instantiation - # assert_equal 2, @accounts.size assert_equal 50, @unknown.credit_limit end 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/invertible_migration_test.rb b/activerecord/test/cases/invertible_migration_test.rb index 0da58040c8..0e5df6bd5b 100644 --- a/activerecord/test/cases/invertible_migration_test.rb +++ b/activerecord/test/cases/invertible_migration_test.rb @@ -5,7 +5,7 @@ end module ActiveRecord class InvertibleMigrationTest < ActiveRecord::TestCase - class SilentMigration < ActiveRecord::Migration + class SilentMigration < ActiveRecord::Migration::Current def write(text = '') # sssshhhhh!! end @@ -105,7 +105,7 @@ module ActiveRecord end end - class LegacyMigration < ActiveRecord::Migration + class LegacyMigration < ActiveRecord::Migration::Current def self.up create_table("horses") do |t| t.column :content, :text 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/migration/change_schema_test.rb b/activerecord/test/cases/migration/change_schema_test.rb index 2ff9cf8cf5..86e691e564 100644 --- a/activerecord/test/cases/migration/change_schema_test.rb +++ b/activerecord/test/cases/migration/change_schema_test.rb @@ -339,7 +339,7 @@ module ActiveRecord def test_change_column_null testing_table_with_only_foo_attribute do - notnull_migration = Class.new(ActiveRecord::Migration) do + notnull_migration = Class.new(ActiveRecord::Migration::Current) do def change change_column_null :testings, :foo, false end diff --git a/activerecord/test/cases/migration/foreign_key_test.rb b/activerecord/test/cases/migration/foreign_key_test.rb index 72f2fa95f1..4e1fbfb690 100644 --- a/activerecord/test/cases/migration/foreign_key_test.rb +++ b/activerecord/test/cases/migration/foreign_key_test.rb @@ -224,7 +224,7 @@ module ActiveRecord assert_match %r{\s+add_foreign_key "astronauts",.+on_update: :cascade,.+on_delete: :nullify$}, output end - class CreateCitiesAndHousesMigration < ActiveRecord::Migration + class CreateCitiesAndHousesMigration < ActiveRecord::Migration::Current def change create_table("cities") { |t| } @@ -243,7 +243,7 @@ module ActiveRecord silence_stream($stdout) { migration.migrate(:down) } end - class CreateSchoolsAndClassesMigration < ActiveRecord::Migration + class CreateSchoolsAndClassesMigration < ActiveRecord::Migration::Current def change create_table(:schools) diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index c3c204cf9f..19be357b6e 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -132,12 +132,12 @@ class MigrationTest < ActiveRecord::TestCase end def test_migration_instance_has_connection - migration = Class.new(ActiveRecord::Migration).new + migration = Class.new(ActiveRecord::Migration::Current).new assert_equal ActiveRecord::Base.connection, migration.connection end def test_method_missing_delegates_to_connection - migration = Class.new(ActiveRecord::Migration) { + migration = Class.new(ActiveRecord::Migration::Current) { def connection Class.new { def create_table; "hi mom!"; end @@ -226,7 +226,7 @@ class MigrationTest < ActiveRecord::TestCase assert_raise(ActiveRecord::StatementInvalid) { Reminder.first } end - class MockMigration < ActiveRecord::Migration + class MockMigration < ActiveRecord::Migration::Current attr_reader :went_up, :went_down def initialize @went_up = false @@ -268,7 +268,7 @@ class MigrationTest < ActiveRecord::TestCase def test_migrator_one_up_with_exception_and_rollback assert_no_column Person, :last_name - migration = Class.new(ActiveRecord::Migration) { + migration = Class.new(ActiveRecord::Migration::Current) { def version; 100 end def migrate(x) add_column "people", "last_name", :string @@ -289,7 +289,7 @@ class MigrationTest < ActiveRecord::TestCase def test_migrator_one_up_with_exception_and_rollback_using_run assert_no_column Person, :last_name - migration = Class.new(ActiveRecord::Migration) { + migration = Class.new(ActiveRecord::Migration::Current) { def version; 100 end def migrate(x) add_column "people", "last_name", :string @@ -310,7 +310,7 @@ class MigrationTest < ActiveRecord::TestCase def test_migration_without_transaction assert_no_column Person, :last_name - migration = Class.new(ActiveRecord::Migration) { + migration = Class.new(ActiveRecord::Migration::Current) { self.disable_ddl_transaction! def version; 101 end @@ -525,7 +525,7 @@ class MigrationTest < ActiveRecord::TestCase if ActiveRecord::Base.connection.supports_advisory_locks? def test_migrator_generates_valid_lock_id - migration = Class.new(ActiveRecord::Migration).new + migration = Class.new(ActiveRecord::Migration::Current).new migrator = ActiveRecord::Migrator.new(:up, [migration], 100) lock_id = migrator.send(:generate_migrator_advisory_lock_id) @@ -539,7 +539,7 @@ class MigrationTest < ActiveRecord::TestCase def test_generate_migrator_advisory_lock_id # It is important we are consistent with how we generate this so that # exclusive locking works across migrator versions - migration = Class.new(ActiveRecord::Migration).new + migration = Class.new(ActiveRecord::Migration::Current).new migrator = ActiveRecord::Migrator.new(:up, [migration], 100) lock_id = migrator.send(:generate_migrator_advisory_lock_id) @@ -556,7 +556,7 @@ class MigrationTest < ActiveRecord::TestCase def test_migrator_one_up_with_unavailable_lock assert_no_column Person, :last_name - migration = Class.new(ActiveRecord::Migration) { + migration = Class.new(ActiveRecord::Migration::Current) { def version; 100 end def migrate(x) add_column "people", "last_name", :string @@ -577,7 +577,7 @@ class MigrationTest < ActiveRecord::TestCase def test_migrator_one_up_with_unavailable_lock_using_run assert_no_column Person, :last_name - migration = Class.new(ActiveRecord::Migration) { + migration = Class.new(ActiveRecord::Migration::Current) { def version; 100 end def migrate(x) add_column "people", "last_name", :string diff --git a/activerecord/test/cases/migrator_test.rb b/activerecord/test/cases/migrator_test.rb index dbf088f455..86eca53141 100644 --- a/activerecord/test/cases/migrator_test.rb +++ b/activerecord/test/cases/migrator_test.rb @@ -6,7 +6,7 @@ class MigratorTest < ActiveRecord::TestCase # Use this class to sense if migrations have gone # up or down. - class Sensor < ActiveRecord::Migration + class Sensor < ActiveRecord::Migration::Current attr_reader :went_up, :went_down def initialize name = self.class.name, version = nil 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/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb index 43f133b12d..9a5e4313d8 100644 --- a/activerecord/test/cases/schema_dumper_test.rb +++ b/activerecord/test/cases/schema_dumper_test.rb @@ -312,7 +312,7 @@ class SchemaDumperTest < ActiveRecord::TestCase end end - class CreateDogMigration < ActiveRecord::Migration + class CreateDogMigration < ActiveRecord::Migration::Current def up create_table("dog_owners") do |t| end @@ -357,7 +357,7 @@ class SchemaDumperTest < ActiveRecord::TestCase def test_schema_dump_with_table_name_prefix_and_ignoring_tables original, $stdout = $stdout, StringIO.new - create_cat_migration = Class.new(ActiveRecord::Migration) do + create_cat_migration = Class.new(ActiveRecord::Migration::Current) do def change create_table("cats") do |t| end 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/activerecord/test/migrations/10_urban/9_add_expressions.rb b/activerecord/test/migrations/10_urban/9_add_expressions.rb index 79a342e574..e908c9eabc 100644 --- a/activerecord/test/migrations/10_urban/9_add_expressions.rb +++ b/activerecord/test/migrations/10_urban/9_add_expressions.rb @@ -1,4 +1,4 @@ -class AddExpressions < ActiveRecord::Migration +class AddExpressions < ActiveRecord::Migration::Current def self.up create_table("expressions") do |t| t.column :expression, :string diff --git a/activerecord/test/migrations/decimal/1_give_me_big_numbers.rb b/activerecord/test/migrations/decimal/1_give_me_big_numbers.rb index 0aed7cbd84..549647de86 100644 --- a/activerecord/test/migrations/decimal/1_give_me_big_numbers.rb +++ b/activerecord/test/migrations/decimal/1_give_me_big_numbers.rb @@ -1,4 +1,4 @@ -class GiveMeBigNumbers < ActiveRecord::Migration +class GiveMeBigNumbers < ActiveRecord::Migration::Current def self.up create_table :big_numbers do |table| table.column :bank_balance, :decimal, :precision => 10, :scale => 2 diff --git a/activerecord/test/migrations/magic/1_currencies_have_symbols.rb b/activerecord/test/migrations/magic/1_currencies_have_symbols.rb index c066c068c2..53b263bf55 100644 --- a/activerecord/test/migrations/magic/1_currencies_have_symbols.rb +++ b/activerecord/test/migrations/magic/1_currencies_have_symbols.rb @@ -1,6 +1,6 @@ # coding: ISO-8859-15 -class CurrenciesHaveSymbols < ActiveRecord::Migration +class CurrenciesHaveSymbols < ActiveRecord::Migration::Current def self.up # We use ¤ for default currency symbol add_column "currencies", "symbol", :string, :default => "¤" diff --git a/activerecord/test/migrations/missing/1000_people_have_middle_names.rb b/activerecord/test/migrations/missing/1000_people_have_middle_names.rb index 4b83d61beb..e046944e31 100644 --- a/activerecord/test/migrations/missing/1000_people_have_middle_names.rb +++ b/activerecord/test/migrations/missing/1000_people_have_middle_names.rb @@ -1,4 +1,4 @@ -class PeopleHaveMiddleNames < ActiveRecord::Migration +class PeopleHaveMiddleNames < ActiveRecord::Migration::Current def self.up add_column "people", "middle_name", :string end diff --git a/activerecord/test/migrations/missing/1_people_have_last_names.rb b/activerecord/test/migrations/missing/1_people_have_last_names.rb index 68209f3ce9..50fe2a9c8e 100644 --- a/activerecord/test/migrations/missing/1_people_have_last_names.rb +++ b/activerecord/test/migrations/missing/1_people_have_last_names.rb @@ -1,4 +1,4 @@ -class PeopleHaveLastNames < ActiveRecord::Migration +class PeopleHaveLastNames < ActiveRecord::Migration::Current def self.up add_column "people", "last_name", :string end diff --git a/activerecord/test/migrations/missing/3_we_need_reminders.rb b/activerecord/test/migrations/missing/3_we_need_reminders.rb index 25bb49cb32..d7c63ac892 100644 --- a/activerecord/test/migrations/missing/3_we_need_reminders.rb +++ b/activerecord/test/migrations/missing/3_we_need_reminders.rb @@ -1,4 +1,4 @@ -class WeNeedReminders < ActiveRecord::Migration +class WeNeedReminders < ActiveRecord::Migration::Current def self.up create_table("reminders") do |t| t.column :content, :text diff --git a/activerecord/test/migrations/missing/4_innocent_jointable.rb b/activerecord/test/migrations/missing/4_innocent_jointable.rb index 002a1bf2a6..20fe183777 100644 --- a/activerecord/test/migrations/missing/4_innocent_jointable.rb +++ b/activerecord/test/migrations/missing/4_innocent_jointable.rb @@ -1,4 +1,4 @@ -class InnocentJointable < ActiveRecord::Migration +class InnocentJointable < ActiveRecord::Migration::Current def self.up create_table("people_reminders", :id => false) do |t| t.column :reminder_id, :integer diff --git a/activerecord/test/migrations/rename/1_we_need_things.rb b/activerecord/test/migrations/rename/1_we_need_things.rb index f5484ac54f..9dce01acfd 100644 --- a/activerecord/test/migrations/rename/1_we_need_things.rb +++ b/activerecord/test/migrations/rename/1_we_need_things.rb @@ -1,4 +1,4 @@ -class WeNeedThings < ActiveRecord::Migration +class WeNeedThings < ActiveRecord::Migration::Current def self.up create_table("things") do |t| t.column :content, :text diff --git a/activerecord/test/migrations/rename/2_rename_things.rb b/activerecord/test/migrations/rename/2_rename_things.rb index 533a113ea8..cb8484e7dc 100644 --- a/activerecord/test/migrations/rename/2_rename_things.rb +++ b/activerecord/test/migrations/rename/2_rename_things.rb @@ -1,4 +1,4 @@ -class RenameThings < ActiveRecord::Migration +class RenameThings < ActiveRecord::Migration::Current def self.up rename_table "things", "awesome_things" end diff --git a/activerecord/test/migrations/to_copy/1_people_have_hobbies.rb b/activerecord/test/migrations/to_copy/1_people_have_hobbies.rb index 639841f663..607113b091 100644 --- a/activerecord/test/migrations/to_copy/1_people_have_hobbies.rb +++ b/activerecord/test/migrations/to_copy/1_people_have_hobbies.rb @@ -1,4 +1,4 @@ -class PeopleHaveLastNames < ActiveRecord::Migration +class PeopleHaveLastNames < ActiveRecord::Migration::Current def self.up add_column "people", "hobbies", :text end diff --git a/activerecord/test/migrations/to_copy/2_people_have_descriptions.rb b/activerecord/test/migrations/to_copy/2_people_have_descriptions.rb index b3d0b30640..d4cbddab50 100644 --- a/activerecord/test/migrations/to_copy/2_people_have_descriptions.rb +++ b/activerecord/test/migrations/to_copy/2_people_have_descriptions.rb @@ -1,4 +1,4 @@ -class PeopleHaveLastNames < ActiveRecord::Migration +class PeopleHaveLastNames < ActiveRecord::Migration::Current def self.up add_column "people", "description", :text end diff --git a/activerecord/test/migrations/to_copy2/1_create_articles.rb b/activerecord/test/migrations/to_copy2/1_create_articles.rb index 0f048d90f7..2e9f5ec6bc 100644 --- a/activerecord/test/migrations/to_copy2/1_create_articles.rb +++ b/activerecord/test/migrations/to_copy2/1_create_articles.rb @@ -1,4 +1,4 @@ -class CreateArticles < ActiveRecord::Migration +class CreateArticles < ActiveRecord::Migration::Current def self.up end diff --git a/activerecord/test/migrations/to_copy2/2_create_comments.rb b/activerecord/test/migrations/to_copy2/2_create_comments.rb index 0f048d90f7..2e9f5ec6bc 100644 --- a/activerecord/test/migrations/to_copy2/2_create_comments.rb +++ b/activerecord/test/migrations/to_copy2/2_create_comments.rb @@ -1,4 +1,4 @@ -class CreateArticles < ActiveRecord::Migration +class CreateArticles < ActiveRecord::Migration::Current def self.up end diff --git a/activerecord/test/migrations/to_copy_with_name_collision/1_people_have_hobbies.rb b/activerecord/test/migrations/to_copy_with_name_collision/1_people_have_hobbies.rb index e438cf5999..8f81805fe1 100644 --- a/activerecord/test/migrations/to_copy_with_name_collision/1_people_have_hobbies.rb +++ b/activerecord/test/migrations/to_copy_with_name_collision/1_people_have_hobbies.rb @@ -1,4 +1,4 @@ -class PeopleHaveLastNames < ActiveRecord::Migration +class PeopleHaveLastNames < ActiveRecord::Migration::Current def self.up add_column "people", "hobbies", :string end diff --git a/activerecord/test/migrations/to_copy_with_timestamps/20090101010101_people_have_hobbies.rb b/activerecord/test/migrations/to_copy_with_timestamps/20090101010101_people_have_hobbies.rb index 639841f663..607113b091 100644 --- a/activerecord/test/migrations/to_copy_with_timestamps/20090101010101_people_have_hobbies.rb +++ b/activerecord/test/migrations/to_copy_with_timestamps/20090101010101_people_have_hobbies.rb @@ -1,4 +1,4 @@ -class PeopleHaveLastNames < ActiveRecord::Migration +class PeopleHaveLastNames < ActiveRecord::Migration::Current def self.up add_column "people", "hobbies", :text end diff --git a/activerecord/test/migrations/to_copy_with_timestamps/20090101010202_people_have_descriptions.rb b/activerecord/test/migrations/to_copy_with_timestamps/20090101010202_people_have_descriptions.rb index b3d0b30640..d4cbddab50 100644 --- a/activerecord/test/migrations/to_copy_with_timestamps/20090101010202_people_have_descriptions.rb +++ b/activerecord/test/migrations/to_copy_with_timestamps/20090101010202_people_have_descriptions.rb @@ -1,4 +1,4 @@ -class PeopleHaveLastNames < ActiveRecord::Migration +class PeopleHaveLastNames < ActiveRecord::Migration::Current def self.up add_column "people", "description", :text end diff --git a/activerecord/test/migrations/to_copy_with_timestamps2/20090101010101_create_articles.rb b/activerecord/test/migrations/to_copy_with_timestamps2/20090101010101_create_articles.rb index 0f048d90f7..2e9f5ec6bc 100644 --- a/activerecord/test/migrations/to_copy_with_timestamps2/20090101010101_create_articles.rb +++ b/activerecord/test/migrations/to_copy_with_timestamps2/20090101010101_create_articles.rb @@ -1,4 +1,4 @@ -class CreateArticles < ActiveRecord::Migration +class CreateArticles < ActiveRecord::Migration::Current def self.up end diff --git a/activerecord/test/migrations/to_copy_with_timestamps2/20090101010202_create_comments.rb b/activerecord/test/migrations/to_copy_with_timestamps2/20090101010202_create_comments.rb index 2b048edbb5..d361847d4b 100644 --- a/activerecord/test/migrations/to_copy_with_timestamps2/20090101010202_create_comments.rb +++ b/activerecord/test/migrations/to_copy_with_timestamps2/20090101010202_create_comments.rb @@ -1,4 +1,4 @@ -class CreateComments < ActiveRecord::Migration +class CreateComments < ActiveRecord::Migration::Current def self.up end diff --git a/activerecord/test/migrations/valid/1_valid_people_have_last_names.rb b/activerecord/test/migrations/valid/1_valid_people_have_last_names.rb index 06cb911117..c450211d8c 100644 --- a/activerecord/test/migrations/valid/1_valid_people_have_last_names.rb +++ b/activerecord/test/migrations/valid/1_valid_people_have_last_names.rb @@ -1,4 +1,4 @@ -class ValidPeopleHaveLastNames < ActiveRecord::Migration +class ValidPeopleHaveLastNames < ActiveRecord::Migration::Current def self.up add_column "people", "last_name", :string end diff --git a/activerecord/test/migrations/valid/2_we_need_reminders.rb b/activerecord/test/migrations/valid/2_we_need_reminders.rb index 25bb49cb32..d7c63ac892 100644 --- a/activerecord/test/migrations/valid/2_we_need_reminders.rb +++ b/activerecord/test/migrations/valid/2_we_need_reminders.rb @@ -1,4 +1,4 @@ -class WeNeedReminders < ActiveRecord::Migration +class WeNeedReminders < ActiveRecord::Migration::Current def self.up create_table("reminders") do |t| t.column :content, :text diff --git a/activerecord/test/migrations/valid/3_innocent_jointable.rb b/activerecord/test/migrations/valid/3_innocent_jointable.rb index 002a1bf2a6..20fe183777 100644 --- a/activerecord/test/migrations/valid/3_innocent_jointable.rb +++ b/activerecord/test/migrations/valid/3_innocent_jointable.rb @@ -1,4 +1,4 @@ -class InnocentJointable < ActiveRecord::Migration +class InnocentJointable < ActiveRecord::Migration::Current def self.up create_table("people_reminders", :id => false) do |t| t.column :reminder_id, :integer diff --git a/activerecord/test/migrations/valid_with_subdirectories/1_valid_people_have_last_names.rb b/activerecord/test/migrations/valid_with_subdirectories/1_valid_people_have_last_names.rb index 06cb911117..c450211d8c 100644 --- a/activerecord/test/migrations/valid_with_subdirectories/1_valid_people_have_last_names.rb +++ b/activerecord/test/migrations/valid_with_subdirectories/1_valid_people_have_last_names.rb @@ -1,4 +1,4 @@ -class ValidPeopleHaveLastNames < ActiveRecord::Migration +class ValidPeopleHaveLastNames < ActiveRecord::Migration::Current def self.up add_column "people", "last_name", :string end diff --git a/activerecord/test/migrations/valid_with_subdirectories/sub/2_we_need_reminders.rb b/activerecord/test/migrations/valid_with_subdirectories/sub/2_we_need_reminders.rb index 25bb49cb32..d7c63ac892 100644 --- a/activerecord/test/migrations/valid_with_subdirectories/sub/2_we_need_reminders.rb +++ b/activerecord/test/migrations/valid_with_subdirectories/sub/2_we_need_reminders.rb @@ -1,4 +1,4 @@ -class WeNeedReminders < ActiveRecord::Migration +class WeNeedReminders < ActiveRecord::Migration::Current def self.up create_table("reminders") do |t| t.column :content, :text diff --git a/activerecord/test/migrations/valid_with_subdirectories/sub1/3_innocent_jointable.rb b/activerecord/test/migrations/valid_with_subdirectories/sub1/3_innocent_jointable.rb index 002a1bf2a6..20fe183777 100644 --- a/activerecord/test/migrations/valid_with_subdirectories/sub1/3_innocent_jointable.rb +++ b/activerecord/test/migrations/valid_with_subdirectories/sub1/3_innocent_jointable.rb @@ -1,4 +1,4 @@ -class InnocentJointable < ActiveRecord::Migration +class InnocentJointable < ActiveRecord::Migration::Current def self.up create_table("people_reminders", :id => false) do |t| t.column :reminder_id, :integer diff --git a/activerecord/test/migrations/valid_with_timestamps/20100101010101_valid_with_timestamps_people_have_last_names.rb b/activerecord/test/migrations/valid_with_timestamps/20100101010101_valid_with_timestamps_people_have_last_names.rb index 1da99ceaba..9fd27593f0 100644 --- a/activerecord/test/migrations/valid_with_timestamps/20100101010101_valid_with_timestamps_people_have_last_names.rb +++ b/activerecord/test/migrations/valid_with_timestamps/20100101010101_valid_with_timestamps_people_have_last_names.rb @@ -1,4 +1,4 @@ -class ValidWithTimestampsPeopleHaveLastNames < ActiveRecord::Migration +class ValidWithTimestampsPeopleHaveLastNames < ActiveRecord::Migration::Current def self.up add_column "people", "last_name", :string end diff --git a/activerecord/test/migrations/valid_with_timestamps/20100201010101_valid_with_timestamps_we_need_reminders.rb b/activerecord/test/migrations/valid_with_timestamps/20100201010101_valid_with_timestamps_we_need_reminders.rb index cb6d735c8b..4a59921136 100644 --- a/activerecord/test/migrations/valid_with_timestamps/20100201010101_valid_with_timestamps_we_need_reminders.rb +++ b/activerecord/test/migrations/valid_with_timestamps/20100201010101_valid_with_timestamps_we_need_reminders.rb @@ -1,4 +1,4 @@ -class ValidWithTimestampsWeNeedReminders < ActiveRecord::Migration +class ValidWithTimestampsWeNeedReminders < ActiveRecord::Migration::Current def self.up create_table("reminders") do |t| t.column :content, :text diff --git a/activerecord/test/migrations/valid_with_timestamps/20100301010101_valid_with_timestamps_innocent_jointable.rb b/activerecord/test/migrations/valid_with_timestamps/20100301010101_valid_with_timestamps_innocent_jointable.rb index 4bd4b4714d..bf934576c9 100644 --- a/activerecord/test/migrations/valid_with_timestamps/20100301010101_valid_with_timestamps_innocent_jointable.rb +++ b/activerecord/test/migrations/valid_with_timestamps/20100301010101_valid_with_timestamps_innocent_jointable.rb @@ -1,4 +1,4 @@ -class ValidWithTimestampsInnocentJointable < ActiveRecord::Migration +class ValidWithTimestampsInnocentJointable < ActiveRecord::Migration::Current def self.up create_table("people_reminders", :id => false) do |t| t.column :reminder_id, :integer diff --git a/activerecord/test/migrations/version_check/20131219224947_migration_version_check.rb b/activerecord/test/migrations/version_check/20131219224947_migration_version_check.rb index 9d46485a31..6f314c881c 100644 --- a/activerecord/test/migrations/version_check/20131219224947_migration_version_check.rb +++ b/activerecord/test/migrations/version_check/20131219224947_migration_version_check.rb @@ -1,4 +1,4 @@ -class MigrationVersionCheck < ActiveRecord::Migration +class MigrationVersionCheck < ActiveRecord::Migration::Current def self.up raise "incorrect migration version" unless version == 20131219224947 end diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index d946fc1bbd..07f7fae5d5 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,19 +1,33 @@ +* Deprecated `Module#qualified_const_` in favour of the builtin Module#const_ + methods. + + *Genadi Samokovarov* + +* Deprecate passing string to define callback. + + *Yuichiro Kaneko* + * `ActiveSupport::Cache::Store#namespaced_key`, `ActiveSupport::Cache::MemCachedStore#escape_key`, and `ActiveSupport::Cache::FileStore#key_file_path` are deprecated and replaced with `normalize_key` that now calls `super`. - + `ActiveSupport::Cache::LocaleCache#set_cache_value` is deprecated and replaced with `write_cache_value`. - + *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. + + This watcher is disabled by default, applications my enable it in the configuration: + + # config/environments/development.rb + config.file_watcher = ActiveSupport::EventedFileUpdateChecker - To opt-in load the [listen](https://github.com/guard/listen) gem in `Gemfile`: + 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/bin/generate_tables b/activesupport/bin/generate_tables index 71a6b78652..2193533588 100755 --- a/activesupport/bin/generate_tables +++ b/activesupport/bin/generate_tables @@ -50,16 +50,11 @@ module ActiveSupport ([0-9A-F]*); # simple lowercase mapping ([0-9A-F]*)$/ix # simple titlecase mapping codepoint.code = $1.hex - #codepoint.name = $2 - #codepoint.category = $3 codepoint.combining_class = Integer($4) - #codepoint.bidi_class = $5 codepoint.decomp_type = $7 codepoint.decomp_mapping = ($8=='') ? nil : $8.split.collect(&:hex) - #codepoint.bidi_mirrored = ($13=='Y') ? true : false codepoint.uppercase_mapping = ($16=='') ? 0 : $16.hex codepoint.lowercase_mapping = ($17=='') ? 0 : $17.hex - #codepoint.titlecase_mapping = ($18=='') ? nil : $18.hex @ucd.codepoints[codepoint.code] = codepoint end diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb index d43fde03a9..bf560ec1fa 100644 --- a/activesupport/lib/active_support/callbacks.rb +++ b/activesupport/lib/active_support/callbacks.rb @@ -295,6 +295,13 @@ module ActiveSupport class Callback #:nodoc:# def self.build(chain, filter, kind, options) + if filter.is_a?(String) + ActiveSupport::Deprecation.warn(<<-MSG.squish) + Passing string to define callback is deprecated and will be removed + in Rails 5.1 without replacement. + MSG + end + new chain.name, filter, kind, options, chain.config end @@ -575,7 +582,7 @@ module ActiveSupport # set_callback :save, :before_meth # # The callback can be specified as a symbol naming an instance method; as a - # proc, lambda, or block; as a string to be instance evaluated; or as an + # proc, lambda, or block; as a string to be instance evaluated(deprecated); or as an # object that responds to a certain method determined by the <tt>:scope</tt> # argument to +define_callbacks+. # diff --git a/activesupport/lib/active_support/core_ext/module/qualified_const.rb b/activesupport/lib/active_support/core_ext/module/qualified_const.rb index 65525013db..3ea39d4267 100644 --- a/activesupport/lib/active_support/core_ext/module/qualified_const.rb +++ b/activesupport/lib/active_support/core_ext/module/qualified_const.rb @@ -3,13 +3,16 @@ require 'active_support/core_ext/string/inflections' #-- # Allows code reuse in the methods below without polluting Module. #++ -module QualifiedConstUtils - def self.raise_if_absolute(path) - raise NameError.new("wrong constant name #$&") if path =~ /\A::[^:]+/ - end - def self.names(path) - path.split('::') +module ActiveSupport + module QualifiedConstUtils + def self.raise_if_absolute(path) + raise NameError.new("wrong constant name #$&") if path =~ /\A::[^:]+/ + end + + def self.names(path) + path.split('::') + end end end @@ -24,9 +27,14 @@ end #++ class Module def qualified_const_defined?(path, search_parents=true) - QualifiedConstUtils.raise_if_absolute(path) + ActiveSupport::Deprecation.warn(<<-MESSAGE.squish) + Module#qualified_const_defined? is deprecated in favour of the builtin + Module#const_defined? and will be removed in Rails 5.1. + MESSAGE - QualifiedConstUtils.names(path).inject(self) do |mod, name| + ActiveSupport::QualifiedConstUtils.raise_if_absolute(path) + + ActiveSupport::QualifiedConstUtils.names(path).inject(self) do |mod, name| return unless mod.const_defined?(name, search_parents) mod.const_get(name) end @@ -34,19 +42,29 @@ class Module end def qualified_const_get(path) - QualifiedConstUtils.raise_if_absolute(path) + ActiveSupport::Deprecation.warn(<<-MESSAGE.squish) + Module#qualified_const_get is deprecated in favour of the builtin + Module#const_get and will be removed in Rails 5.1. + MESSAGE + + ActiveSupport::QualifiedConstUtils.raise_if_absolute(path) - QualifiedConstUtils.names(path).inject(self) do |mod, name| + ActiveSupport::QualifiedConstUtils.names(path).inject(self) do |mod, name| mod.const_get(name) end end def qualified_const_set(path, value) - QualifiedConstUtils.raise_if_absolute(path) + ActiveSupport::Deprecation.warn(<<-MESSAGE.squish) + Module#qualified_const_set is deprecated in favour of the builtin + Module#const_set and will be removed in Rails 5.1. + MESSAGE + + ActiveSupport::QualifiedConstUtils.raise_if_absolute(path) const_name = path.demodulize mod_name = path.deconstantize - mod = mod_name.empty? ? self : qualified_const_get(mod_name) + mod = mod_name.empty? ? self : const_get(mod_name) mod.const_set(const_name, value) end end diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb index 675db8a36b..768c9a1b2c 100644 --- a/activesupport/lib/active_support/core_ext/time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/time/calculations.rb @@ -162,7 +162,6 @@ class Time # Returns a new Time representing the start of the day (0:00) def beginning_of_day - #(self - seconds_since_midnight).change(usec: 0) change(:hour => 0) end alias :midnight :beginning_of_day 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/activesupport/test/callbacks_test.rb b/activesupport/test/callbacks_test.rb index 3b00ff87a0..a624473f46 100644 --- a/activesupport/test/callbacks_test.rb +++ b/activesupport/test/callbacks_test.rb @@ -59,7 +59,7 @@ module CallbacksTest [:before_save, :after_save].each do |callback_method| callback_method_sym = callback_method.to_sym send(callback_method, callback_symbol(callback_method_sym)) - send(callback_method, callback_string(callback_method_sym)) + ActiveSupport::Deprecation.silence { send(callback_method, callback_string(callback_method_sym)) } send(callback_method, callback_proc(callback_method_sym)) send(callback_method, callback_object(callback_method_sym.to_s.gsub(/_save/, ''))) send(callback_method, CallbackClass) @@ -228,7 +228,7 @@ module CallbacksTest set_callback :save, :before, :nope, :if => :no set_callback :save, :before, :nope, :unless => :yes set_callback :save, :after, :tweedle - set_callback :save, :before, "tweedle_dee" + ActiveSupport::Deprecation.silence { set_callback :save, :before, "tweedle_dee" } set_callback :save, :before, proc {|m| m.history << "yup" } set_callback :save, :before, :nope, :if => proc { false } set_callback :save, :before, :nope, :unless => proc { true } @@ -1046,7 +1046,7 @@ module CallbacksTest def test_add_eval calls = [] - klass = build_class("bar") + klass = ActiveSupport::Deprecation.silence { build_class("bar") } klass.class_eval { define_method(:bar) { calls << klass } } klass.new.run assert_equal 1, calls.length @@ -1086,7 +1086,7 @@ module CallbacksTest def test_skip_string # raises error calls = [] - klass = build_class("bar") + klass = ActiveSupport::Deprecation.silence { build_class("bar") } klass.class_eval { define_method(:bar) { calls << klass } } assert_raises(ArgumentError) { klass.skip "bar" } klass.new.run @@ -1111,4 +1111,14 @@ module CallbacksTest assert_equal 1, calls.length end end + + class DeprecatedWarningTest < ActiveSupport::TestCase + def test_deprecate_string_callback + klass = Class.new(Record) + + assert_deprecated do + klass.send :before_save, "tweedle_dee" + end + end + end end diff --git a/activesupport/test/core_ext/module/qualified_const_test.rb b/activesupport/test/core_ext/module/qualified_const_test.rb index 37c9228a64..a3146cabe1 100644 --- a/activesupport/test/core_ext/module/qualified_const_test.rb +++ b/activesupport/test/core_ext/module/qualified_const_test.rb @@ -19,84 +19,94 @@ end class QualifiedConstTest < ActiveSupport::TestCase test "Object.qualified_const_defined?" do - assert Object.qualified_const_defined?("QualifiedConstTestMod") - assert !Object.qualified_const_defined?("NonExistingQualifiedConstTestMod") - - assert Object.qualified_const_defined?("QualifiedConstTestMod::X") - assert !Object.qualified_const_defined?("QualifiedConstTestMod::Y") - - assert Object.qualified_const_defined?("QualifiedConstTestMod::M::X") - assert !Object.qualified_const_defined?("QualifiedConstTestMod::M::Y") - - if Module.method(:const_defined?).arity == 1 - assert !Object.qualified_const_defined?("QualifiedConstTestMod::N::X") - else - assert Object.qualified_const_defined?("QualifiedConstTestMod::N::X") - assert !Object.qualified_const_defined?("QualifiedConstTestMod::N::X", false) - assert Object.qualified_const_defined?("QualifiedConstTestMod::N::X", true) + assert_deprecated do + assert Object.qualified_const_defined?("QualifiedConstTestMod") + assert !Object.qualified_const_defined?("NonExistingQualifiedConstTestMod") + + assert Object.qualified_const_defined?("QualifiedConstTestMod::X") + assert !Object.qualified_const_defined?("QualifiedConstTestMod::Y") + + assert Object.qualified_const_defined?("QualifiedConstTestMod::M::X") + assert !Object.qualified_const_defined?("QualifiedConstTestMod::M::Y") + + if Module.method(:const_defined?).arity == 1 + assert !Object.qualified_const_defined?("QualifiedConstTestMod::N::X") + else + assert Object.qualified_const_defined?("QualifiedConstTestMod::N::X") + assert !Object.qualified_const_defined?("QualifiedConstTestMod::N::X", false) + assert Object.qualified_const_defined?("QualifiedConstTestMod::N::X", true) + end end end test "mod.qualified_const_defined?" do - assert QualifiedConstTestMod.qualified_const_defined?("M") - assert !QualifiedConstTestMod.qualified_const_defined?("NonExistingM") - - assert QualifiedConstTestMod.qualified_const_defined?("M::X") - assert !QualifiedConstTestMod.qualified_const_defined?("M::Y") - - assert QualifiedConstTestMod.qualified_const_defined?("M::C::X") - assert !QualifiedConstTestMod.qualified_const_defined?("M::C::Y") - - if Module.method(:const_defined?).arity == 1 - assert !QualifiedConstTestMod.qualified_const_defined?("QualifiedConstTestMod::N::X") - else - assert QualifiedConstTestMod.qualified_const_defined?("N::X") - assert !QualifiedConstTestMod.qualified_const_defined?("N::X", false) - assert QualifiedConstTestMod.qualified_const_defined?("N::X", true) + assert_deprecated do + assert QualifiedConstTestMod.qualified_const_defined?("M") + assert !QualifiedConstTestMod.qualified_const_defined?("NonExistingM") + + assert QualifiedConstTestMod.qualified_const_defined?("M::X") + assert !QualifiedConstTestMod.qualified_const_defined?("M::Y") + + assert QualifiedConstTestMod.qualified_const_defined?("M::C::X") + assert !QualifiedConstTestMod.qualified_const_defined?("M::C::Y") + + if Module.method(:const_defined?).arity == 1 + assert !QualifiedConstTestMod.qualified_const_defined?("QualifiedConstTestMod::N::X") + else + assert QualifiedConstTestMod.qualified_const_defined?("N::X") + assert !QualifiedConstTestMod.qualified_const_defined?("N::X", false) + assert QualifiedConstTestMod.qualified_const_defined?("N::X", true) + end end end test "qualified_const_get" do - assert_equal false, Object.qualified_const_get("QualifiedConstTestMod::X") - assert_equal false, QualifiedConstTestMod.qualified_const_get("X") - assert_equal 1, QualifiedConstTestMod.qualified_const_get("M::X") - assert_equal 1, QualifiedConstTestMod.qualified_const_get("N::X") - assert_equal 2, QualifiedConstTestMod.qualified_const_get("M::C::X") - - assert_raise(NameError) { QualifiedConstTestMod.qualified_const_get("M::C::Y")} + assert_deprecated do + assert_equal false, Object.qualified_const_get("QualifiedConstTestMod::X") + assert_equal false, QualifiedConstTestMod.qualified_const_get("X") + assert_equal 1, QualifiedConstTestMod.qualified_const_get("M::X") + assert_equal 1, QualifiedConstTestMod.qualified_const_get("N::X") + assert_equal 2, QualifiedConstTestMod.qualified_const_get("M::C::X") + + assert_raise(NameError) { QualifiedConstTestMod.qualified_const_get("M::C::Y")} + end end test "qualified_const_set" do - begin - m = Module.new - assert_equal m, Object.qualified_const_set("QualifiedConstTestMod2", m) - assert_equal m, ::QualifiedConstTestMod2 - - # We are going to assign to existing constants on purpose, so silence warnings. - silence_warnings do - assert_equal true, QualifiedConstTestMod.qualified_const_set("QualifiedConstTestMod::X", true) - assert_equal true, QualifiedConstTestMod::X - - assert_equal 10, QualifiedConstTestMod::M.qualified_const_set("X", 10) - assert_equal 10, QualifiedConstTestMod::M::X - end - ensure - silence_warnings do - QualifiedConstTestMod.qualified_const_set('QualifiedConstTestMod::X', false) - QualifiedConstTestMod::M.qualified_const_set('X', 1) + assert_deprecated do + begin + m = Module.new + assert_equal m, Object.qualified_const_set("QualifiedConstTestMod2", m) + assert_equal m, ::QualifiedConstTestMod2 + + # We are going to assign to existing constants on purpose, so silence warnings. + silence_warnings do + assert_equal true, QualifiedConstTestMod.qualified_const_set("QualifiedConstTestMod::X", true) + assert_equal true, QualifiedConstTestMod::X + + assert_equal 10, QualifiedConstTestMod::M.qualified_const_set("X", 10) + assert_equal 10, QualifiedConstTestMod::M::X + end + ensure + silence_warnings do + QualifiedConstTestMod.qualified_const_set('QualifiedConstTestMod::X', false) + QualifiedConstTestMod::M.qualified_const_set('X', 1) + end end end end test "reject absolute paths" do - assert_raise_with_message(NameError, "wrong constant name ::X") { Object.qualified_const_defined?("::X")} - assert_raise_with_message(NameError, "wrong constant name ::X") { Object.qualified_const_defined?("::X::Y")} + assert_deprecated do + assert_raise_with_message(NameError, "wrong constant name ::X") { Object.qualified_const_defined?("::X")} + assert_raise_with_message(NameError, "wrong constant name ::X") { Object.qualified_const_defined?("::X::Y")} - assert_raise_with_message(NameError, "wrong constant name ::X") { Object.qualified_const_get("::X")} - assert_raise_with_message(NameError, "wrong constant name ::X") { Object.qualified_const_get("::X::Y")} + assert_raise_with_message(NameError, "wrong constant name ::X") { Object.qualified_const_get("::X")} + assert_raise_with_message(NameError, "wrong constant name ::X") { Object.qualified_const_get("::X::Y")} - assert_raise_with_message(NameError, "wrong constant name ::X") { Object.qualified_const_set("::X", nil)} - assert_raise_with_message(NameError, "wrong constant name ::X") { Object.qualified_const_set("::X::Y", nil)} + assert_raise_with_message(NameError, "wrong constant name ::X") { Object.qualified_const_set("::X", nil)} + assert_raise_with_message(NameError, "wrong constant name ::X") { Object.qualified_const_set("::X::Y", nil)} + end end private diff --git a/guides/source/active_record_basics.md b/guides/source/active_record_basics.md index dafbe17bbd..8bd135ddd5 100644 --- a/guides/source/active_record_basics.md +++ b/guides/source/active_record_basics.md @@ -132,10 +132,10 @@ Creating Active Record Models ----------------------------- It is very easy to create Active Record models. All you have to do is to -subclass the `ActiveRecord::Base` class and you're good to go: +subclass the `ApplicationRecord` class and you're good to go: ```ruby -class Product < ActiveRecord::Base +class Product < ApplicationRecord end ``` @@ -168,11 +168,12 @@ What if you need to follow a different naming convention or need to use your Rails application with a legacy database? No problem, you can easily override the default conventions. -You can use the `ActiveRecord::Base.table_name=` method to specify the table -name that should be used: +`ApplicationRecord` inherits from `ActionController::Base`, which defines a +number of helpful methods. You can use the `ActiveRecord::Base.table_name=` +method to specify the table name that should be used: ```ruby -class Product < ActiveRecord::Base +class Product < ApplicationRecord self.table_name = "my_products" end ``` @@ -193,7 +194,7 @@ It's also possible to override the column that should be used as the table's primary key using the `ActiveRecord::Base.primary_key=` method: ```ruby -class Product < ActiveRecord::Base +class Product < ApplicationRecord self.primary_key = "product_id" end ``` @@ -320,7 +321,7 @@ they raise the exception `ActiveRecord::RecordInvalid` if validation fails. A quick example to illustrate: ```ruby -class User < ActiveRecord::Base +class User < ApplicationRecord validates :name, presence: true end @@ -350,7 +351,7 @@ database that Active Record supports using `rake`. Here's a migration that creates a table: ```ruby -class CreatePublications < ActiveRecord::Migration +class CreatePublications < ActiveRecord::Migration[5.0] def change create_table :publications do |t| t.string :title diff --git a/guides/source/active_record_callbacks.md b/guides/source/active_record_callbacks.md index b5ad3e9411..d95c6c0e78 100644 --- a/guides/source/active_record_callbacks.md +++ b/guides/source/active_record_callbacks.md @@ -31,7 +31,7 @@ Callbacks are methods that get called at certain moments of an object's life cyc In order to use the available callbacks, you need to register them. You can implement the callbacks as ordinary methods and use a macro-style class method to register them as callbacks: ```ruby -class User < ActiveRecord::Base +class User < ApplicationRecord validates :login, :email, presence: true before_validation :ensure_login_has_a_value @@ -48,7 +48,7 @@ end The macro-style class methods can also receive a block. Consider using this style if the code inside your block is so short that it fits in a single line: ```ruby -class User < ActiveRecord::Base +class User < ApplicationRecord validates :login, :email, presence: true before_create do @@ -60,7 +60,7 @@ end Callbacks can also be registered to only fire on certain life cycle events: ```ruby -class User < ActiveRecord::Base +class User < ApplicationRecord before_validation :normalize_name, on: :create # :on takes an array as well @@ -126,7 +126,7 @@ The `after_find` callback will be called whenever Active Record loads a record f The `after_initialize` and `after_find` callbacks have no `before_*` counterparts, but they can be registered just like the other Active Record callbacks. ```ruby -class User < ActiveRecord::Base +class User < ApplicationRecord after_initialize do |user| puts "You have initialized an object!" end @@ -151,7 +151,7 @@ You have initialized an object! The `after_touch` callback will be called whenever an Active Record object is touched. ```ruby -class User < ActiveRecord::Base +class User < ApplicationRecord after_touch do |user| puts "You have touched an object" end @@ -168,14 +168,14 @@ You have touched an object It can be used along with `belongs_to`: ```ruby -class Employee < ActiveRecord::Base +class Employee < ApplicationRecord belongs_to :company, touch: true after_touch do puts 'An Employee was touched' end end -class Company < ActiveRecord::Base +class Company < ApplicationRecord has_many :employees after_touch :log_when_employees_or_company_touched @@ -266,11 +266,11 @@ Relational Callbacks Callbacks work through model relationships, and can even be defined by them. Suppose an example where a user has many articles. A user's articles should be destroyed if the user is destroyed. Let's add an `after_destroy` callback to the `User` model by way of its relationship to the `Article` model: ```ruby -class User < ActiveRecord::Base +class User < ApplicationRecord has_many :articles, dependent: :destroy end -class Article < ActiveRecord::Base +class Article < ApplicationRecord after_destroy :log_destroy_action def log_destroy_action @@ -297,7 +297,7 @@ As with validations, we can also make the calling of a callback method condition You can associate the `:if` and `:unless` options with a symbol corresponding to the name of a predicate method that will get called right before the callback. When using the `:if` option, the callback won't be executed if the predicate method returns false; when using the `:unless` option, the callback won't be executed if the predicate method returns true. This is the most common option. Using this form of registration it is also possible to register several different predicates that should be called to check if the callback should be executed. ```ruby -class Order < ActiveRecord::Base +class Order < ApplicationRecord before_save :normalize_card_number, if: :paid_with_card? end ``` @@ -307,7 +307,7 @@ end You can also use a string that will be evaluated using `eval` and hence needs to contain valid Ruby code. You should use this option only when the string represents a really short condition: ```ruby -class Order < ActiveRecord::Base +class Order < ApplicationRecord before_save :normalize_card_number, if: "paid_with_card?" end ``` @@ -317,7 +317,7 @@ end Finally, it is possible to associate `:if` and `:unless` with a `Proc` object. This option is best suited when writing short validation methods, usually one-liners: ```ruby -class Order < ActiveRecord::Base +class Order < ApplicationRecord before_save :normalize_card_number, if: Proc.new { |order| order.paid_with_card? } end @@ -328,7 +328,7 @@ end When writing conditional callbacks, it is possible to mix both `:if` and `:unless` in the same callback declaration: ```ruby -class Comment < ActiveRecord::Base +class Comment < ApplicationRecord after_create :send_email_to_author, if: :author_wants_emails?, unless: Proc.new { |comment| comment.article.ignore_comments? } end @@ -354,7 +354,7 @@ end When declared inside a class, as above, the callback methods will receive the model object as a parameter. We can now use the callback class in the model: ```ruby -class PictureFile < ActiveRecord::Base +class PictureFile < ApplicationRecord after_destroy PictureFileCallbacks.new end ``` @@ -374,7 +374,7 @@ end If the callback method is declared this way, it won't be necessary to instantiate a `PictureFileCallbacks` object. ```ruby -class PictureFile < ActiveRecord::Base +class PictureFile < ApplicationRecord after_destroy PictureFileCallbacks end ``` @@ -398,7 +398,7 @@ end By using the `after_commit` callback we can account for this case. ```ruby -class PictureFile < ActiveRecord::Base +class PictureFile < ApplicationRecord after_commit :delete_picture_file_from_disk, on: [:destroy] def delete_picture_file_from_disk @@ -420,7 +420,7 @@ common, there are aliases for those operations: * `after_destroy_commit` ```ruby -class PictureFile < ActiveRecord::Base +class PictureFile < ApplicationRecord after_destroy_commit :delete_picture_file_from_disk def delete_picture_file_from_disk diff --git a/guides/source/active_record_migrations.md b/guides/source/active_record_migrations.md index 5aa5dc4f60..a8ffa5b378 100644 --- a/guides/source/active_record_migrations.md +++ b/guides/source/active_record_migrations.md @@ -35,7 +35,7 @@ history to the latest version. Active Record will also update your Here's an example of a migration: ```ruby -class CreateProducts < ActiveRecord::Migration +class CreateProducts < ActiveRecord::Migration[5.0] def change create_table :products do |t| t.string :name @@ -72,7 +72,7 @@ If you wish for a migration to do something that Active Record doesn't know how to reverse, you can use `reversible`: ```ruby -class ChangeProductsPrice < ActiveRecord::Migration +class ChangeProductsPrice < ActiveRecord::Migration[5.0] def change reversible do |dir| change_table :products do |t| @@ -87,7 +87,7 @@ end Alternatively, you can use `up` and `down` instead of `change`: ```ruby -class ChangeProductsPrice < ActiveRecord::Migration +class ChangeProductsPrice < ActiveRecord::Migration[5.0] def up change_table :products do |t| t.change :price, :string @@ -129,7 +129,7 @@ $ bin/rails generate migration AddPartNumberToProducts This will create an empty but appropriately named migration: ```ruby -class AddPartNumberToProducts < ActiveRecord::Migration +class AddPartNumberToProducts < ActiveRecord::Migration[5.0] def change end end @@ -146,7 +146,7 @@ $ bin/rails generate migration AddPartNumberToProducts part_number:string will generate ```ruby -class AddPartNumberToProducts < ActiveRecord::Migration +class AddPartNumberToProducts < ActiveRecord::Migration[5.0] def change add_column :products, :part_number, :string end @@ -162,7 +162,7 @@ $ bin/rails generate migration AddPartNumberToProducts part_number:string:index will generate ```ruby -class AddPartNumberToProducts < ActiveRecord::Migration +class AddPartNumberToProducts < ActiveRecord::Migration[5.0] def change add_column :products, :part_number, :string add_index :products, :part_number @@ -180,7 +180,7 @@ $ bin/rails generate migration RemovePartNumberFromProducts part_number:string generates ```ruby -class RemovePartNumberFromProducts < ActiveRecord::Migration +class RemovePartNumberFromProducts < ActiveRecord::Migration[5.0] def change remove_column :products, :part_number, :string end @@ -196,7 +196,7 @@ $ bin/rails generate migration AddDetailsToProducts part_number:string price:dec generates ```ruby -class AddDetailsToProducts < ActiveRecord::Migration +class AddDetailsToProducts < ActiveRecord::Migration[5.0] def change add_column :products, :part_number, :string add_column :products, :price, :decimal @@ -215,7 +215,7 @@ $ bin/rails generate migration CreateProducts name:string part_number:string generates ```ruby -class CreateProducts < ActiveRecord::Migration +class CreateProducts < ActiveRecord::Migration[5.0] def change create_table :products do |t| t.string :name @@ -239,7 +239,7 @@ $ bin/rails generate migration AddUserRefToProducts user:references generates ```ruby -class AddUserRefToProducts < ActiveRecord::Migration +class AddUserRefToProducts < ActiveRecord::Migration[5.0] def change add_reference :products, :user, index: true, foreign_key: true end @@ -257,7 +257,7 @@ $ bin/rails g migration CreateJoinTableCustomerProduct customer product will produce the following migration: ```ruby -class CreateJoinTableCustomerProduct < ActiveRecord::Migration +class CreateJoinTableCustomerProduct < ActiveRecord::Migration[5.0] def change create_join_table :customers, :products do |t| # t.index [:customer_id, :product_id] @@ -281,7 +281,7 @@ $ bin/rails generate model Product name:string description:text will create a migration that looks like this ```ruby -class CreateProducts < ActiveRecord::Migration +class CreateProducts < ActiveRecord::Migration[5.0] def change create_table :products do |t| t.string :name @@ -309,7 +309,7 @@ $ bin/rails generate migration AddDetailsToProducts 'price:decimal{5,2}' supplie will produce a migration that looks like this ```ruby -class AddDetailsToProducts < ActiveRecord::Migration +class AddDetailsToProducts < ActiveRecord::Migration[5.0] def change add_column :products, :price, :decimal, precision: 5, scale: 2 add_reference :products, :supplier, polymorphic: true, index: true @@ -563,7 +563,7 @@ to reverse. You can use `reversible` to specify what to do when running a migration and what else to do when reverting it. For example: ```ruby -class ExampleMigration < ActiveRecord::Migration +class ExampleMigration < ActiveRecord::Migration[5.0] def change create_table :distributors do |t| t.string :zipcode @@ -616,7 +616,7 @@ is wise to perform the transformations in precisely the reverse order they were made in the `up` method. The example in the `reversible` section is equivalent to: ```ruby -class ExampleMigration < ActiveRecord::Migration +class ExampleMigration < ActiveRecord::Migration[5.0] def up create_table :distributors do |t| t.string :zipcode @@ -659,7 +659,7 @@ You can use Active Record's ability to rollback migrations using the `revert` me ```ruby require_relative '20121212123456_example_migration' -class FixupExampleMigration < ActiveRecord::Migration +class FixupExampleMigration < ActiveRecord::Migration[5.0] def change revert ExampleMigration @@ -677,7 +677,7 @@ is later decided it would be best to use Active Record validations, in place of the `CHECK` constraint, to verify the zipcode. ```ruby -class DontUseConstraintForZipcodeValidationMigration < ActiveRecord::Migration +class DontUseConstraintForZipcodeValidationMigration < ActiveRecord::Migration[5.0] def change revert do # copy-pasted code from ExampleMigration @@ -841,7 +841,7 @@ Several methods are provided in migrations that allow you to control all this: For example, this migration: ```ruby -class CreateProducts < ActiveRecord::Migration +class CreateProducts < ActiveRecord::Migration[5.0] def change suppress_messages do create_table :products do |t| @@ -1015,7 +1015,7 @@ to add or modify data. This is useful in an existing database that can't be dest and recreated, such as a production database. ```ruby -class AddInitialProducts < ActiveRecord::Migration +class AddInitialProducts < ActiveRecord::Migration[5.0] def up 5.times do |i| Product.create(name: "Product ##{i}", description: "A product.") diff --git a/guides/source/active_record_postgresql.md b/guides/source/active_record_postgresql.md index 742db7be32..b592209d4b 100644 --- a/guides/source/active_record_postgresql.md +++ b/guides/source/active_record_postgresql.md @@ -39,7 +39,7 @@ create_table :documents do |t| end # app/models/document.rb -class Document < ActiveRecord::Base +class Document < ApplicationRecord end # Usage @@ -63,7 +63,7 @@ add_index :books, :tags, using: 'gin' add_index :books, :ratings, using: 'gin' # app/models/book.rb -class Book < ActiveRecord::Base +class Book < ApplicationRecord end # Usage @@ -97,7 +97,7 @@ ActiveRecord::Schema.define do end # app/models/profile.rb -class Profile < ActiveRecord::Base +class Profile < ApplicationRecord end # Usage @@ -122,7 +122,7 @@ create_table :events do |t| end # app/models/event.rb -class Event < ActiveRecord::Base +class Event < ApplicationRecord end # Usage @@ -150,7 +150,7 @@ create_table :events do |t| end # app/models/event.rb -class Event < ActiveRecord::Base +class Event < ApplicationRecord end # Usage @@ -200,7 +200,7 @@ create_table :contacts do |t| end # app/models/contact.rb -class Contact < ActiveRecord::Base +class Contact < ApplicationRecord end # Usage @@ -239,7 +239,7 @@ def down end # app/models/article.rb -class Article < ActiveRecord::Base +class Article < ApplicationRecord end # Usage @@ -294,7 +294,7 @@ create_table :revisions do |t| end # app/models/revision.rb -class Revision < ActiveRecord::Base +class Revision < ApplicationRecord end # Usage @@ -317,12 +317,12 @@ create_table :comments, id: :uuid, default: 'gen_random_uuid()' do |t| end # app/models/post.rb -class Post < ActiveRecord::Base +class Post < ApplicationRecord has_many :comments end # app/models/comment.rb -class Comment < ActiveRecord::Base +class Comment < ApplicationRecord belongs_to :post end ``` @@ -341,7 +341,7 @@ create_table :users, force: true do |t| end # app/models/device.rb -class User < ActiveRecord::Base +class User < ApplicationRecord end # Usage @@ -370,7 +370,7 @@ create_table(:devices, force: true) do |t| end # app/models/device.rb -class Device < ActiveRecord::Base +class Device < ApplicationRecord end # Usage @@ -410,7 +410,7 @@ create_table :devices, id: :uuid, default: 'gen_random_uuid()' do |t| end # app/models/device.rb -class Device < ActiveRecord::Base +class Device < ApplicationRecord end # Usage @@ -434,7 +434,7 @@ end execute "CREATE INDEX documents_idx ON documents USING gin(to_tsvector('english', title || ' ' || body));" # app/models/document.rb -class Document < ActiveRecord::Base +class Document < ApplicationRecord end # Usage @@ -484,7 +484,7 @@ CREATE VIEW articles AS SQL # app/models/article.rb -class Article < ActiveRecord::Base +class Article < ApplicationRecord self.primary_key = "id" def archive! update_attribute :archived, true diff --git a/guides/source/active_record_querying.md b/guides/source/active_record_querying.md index ed1c3e7061..4606ac4683 100644 --- a/guides/source/active_record_querying.md +++ b/guides/source/active_record_querying.md @@ -25,7 +25,7 @@ Code examples throughout this guide will refer to one or more of the following m TIP: All of the following models use `id` as the primary key, unless specified otherwise. ```ruby -class Client < ActiveRecord::Base +class Client < ApplicationRecord has_one :address has_many :orders has_and_belongs_to_many :roles @@ -33,19 +33,19 @@ end ``` ```ruby -class Address < ActiveRecord::Base +class Address < ApplicationRecord belongs_to :client end ``` ```ruby -class Order < ActiveRecord::Base +class Order < ApplicationRecord belongs_to :client, counter_cache: true end ``` ```ruby -class Role < ActiveRecord::Base +class Role < ApplicationRecord has_and_belongs_to_many :clients end ``` @@ -740,7 +740,7 @@ SELECT "articles".* FROM "articles" WHERE (id > 10) ORDER BY id desc LIMIT 20 The `reorder` method overrides the default scope order. For example: ```ruby -class Article < ActiveRecord::Base +class Article < ApplicationRecord has_many :comments, -> { order('posted_at DESC') } end @@ -889,7 +889,7 @@ This behavior can be turned off by setting `ActiveRecord::Base.lock_optimistical To override the name of the `lock_version` column, `ActiveRecord::Base` provides a class attribute called `locking_column`: ```ruby -class Client < ActiveRecord::Base +class Client < ApplicationRecord self.locking_column = :lock_client_column end ``` @@ -970,26 +970,26 @@ Active Record lets you use the names of the [associations](association_basics.ht For example, consider the following `Category`, `Article`, `Comment`, `Guest` and `Tag` models: ```ruby -class Category < ActiveRecord::Base +class Category < ApplicationRecord has_many :articles end -class Article < ActiveRecord::Base +class Article < ApplicationRecord belongs_to :category has_many :comments has_many :tags end -class Comment < ActiveRecord::Base +class Comment < ApplicationRecord belongs_to :article has_one :guest end -class Guest < ActiveRecord::Base +class Guest < ApplicationRecord belongs_to :comment end -class Tag < ActiveRecord::Base +class Tag < ApplicationRecord belongs_to :article end ``` @@ -1199,7 +1199,7 @@ Scoping allows you to specify commonly-used queries which can be referenced as m To define a simple scope, we use the `scope` method inside the class, passing the query that we'd like to run when this scope is called: ```ruby -class Article < ActiveRecord::Base +class Article < ApplicationRecord scope :published, -> { where(published: true) } end ``` @@ -1207,7 +1207,7 @@ end This is exactly the same as defining a class method, and which you use is a matter of personal preference: ```ruby -class Article < ActiveRecord::Base +class Article < ApplicationRecord def self.published where(published: true) end @@ -1217,7 +1217,7 @@ end Scopes are also chainable within scopes: ```ruby -class Article < ActiveRecord::Base +class Article < ApplicationRecord scope :published, -> { where(published: true) } scope :published_and_commented, -> { published.where("comments_count > 0") } end @@ -1241,7 +1241,7 @@ category.articles.published # => [published articles belonging to this category] Your scope can take arguments: ```ruby -class Article < ActiveRecord::Base +class Article < ApplicationRecord scope :created_before, ->(time) { where("created_at < ?", time) } end ``` @@ -1255,7 +1255,7 @@ Article.created_before(Time.zone.now) However, this is just duplicating the functionality that would be provided to you by a class method. ```ruby -class Article < ActiveRecord::Base +class Article < ApplicationRecord def self.created_before(time) where("created_at < ?", time) end @@ -1274,7 +1274,7 @@ If we wish for a scope to be applied across all queries to the model we can use `default_scope` method within the model itself. ```ruby -class Client < ActiveRecord::Base +class Client < ApplicationRecord default_scope { where("removed_at IS NULL") } end ``` @@ -1290,7 +1290,7 @@ If you need to do more complex things with a default scope, you can alternativel define it as a class method: ```ruby -class Client < ActiveRecord::Base +class Client < ApplicationRecord def self.default_scope # Should return an ActiveRecord::Relation. end @@ -1301,7 +1301,7 @@ NOTE: The `default_scope` is also applied while creating/building a record. It is not applied while updating a record. E.g.: ```ruby -class Client < ActiveRecord::Base +class Client < ApplicationRecord default_scope { where(active: true) } end @@ -1314,7 +1314,7 @@ Client.unscoped.new # => #<Client id: nil, active: nil> Just like `where` clauses scopes are merged using `AND` conditions. ```ruby -class User < ActiveRecord::Base +class User < ApplicationRecord scope :active, -> { where state: 'active' } scope :inactive, -> { where state: 'inactive' } end @@ -1343,7 +1343,7 @@ One important caveat is that `default_scope` will be prepended in `scope` and `where` conditions. ```ruby -class User < ActiveRecord::Base +class User < ApplicationRecord default_scope { where state: 'pending' } scope :active, -> { where state: 'active' } scope :inactive, -> { where state: 'inactive' } @@ -1405,7 +1405,7 @@ Enums The `enum` macro maps an integer column to a set of possible values. ```ruby -class Book < ActiveRecord::Base +class Book < ApplicationRecord enum availability: [:available, :unavailable] end ``` @@ -1657,7 +1657,7 @@ a large or often-running query. However, any model method overrides will not be available. For example: ```ruby -class Client < ActiveRecord::Base +class Client < ApplicationRecord def name "I am #{super}" end @@ -1692,7 +1692,7 @@ Person.ids ``` ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord self.primary_key = "person_id" end diff --git a/guides/source/active_record_validations.md b/guides/source/active_record_validations.md index ec31385077..dd7adf09a2 100644 --- a/guides/source/active_record_validations.md +++ b/guides/source/active_record_validations.md @@ -20,7 +20,7 @@ Validations Overview Here's an example of a very simple validation: ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :name, presence: true end @@ -80,7 +80,7 @@ method to determine whether an object is already in the database or not. Consider the following simple Active Record class: ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord end ``` @@ -157,7 +157,7 @@ and returns true if no errors were found in the object, and false otherwise. As you saw above: ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :name, presence: true end @@ -175,7 +175,7 @@ even if it's technically invalid, because validations are automatically run only when the object is saved, such as with the `create` or `save` methods. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :name, presence: true end @@ -221,7 +221,7 @@ it doesn't verify the validity of the object as a whole. It only checks to see whether there are errors found on an individual attribute of the object. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :name, presence: true end @@ -239,7 +239,7 @@ To check which validations failed on an invalid attribute, you can use key to get the symbol of the validator: ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :name, presence: true end @@ -285,7 +285,7 @@ the field does exist in your database, the `accept` option must be set to `true` or else the validation will not run. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :terms_of_service, acceptance: true end ``` @@ -297,7 +297,7 @@ It can receive an `:accept` option, which determines the value that will be considered acceptance. It defaults to "1" and can be easily changed. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :terms_of_service, acceptance: { accept: 'yes' } end ``` @@ -309,7 +309,7 @@ and they also need to be validated. When you try to save your object, `valid?` will be called upon each one of the associated objects. ```ruby -class Library < ActiveRecord::Base +class Library < ApplicationRecord has_many :books validates_associated :books end @@ -332,7 +332,7 @@ or a password. This validation creates a virtual attribute whose name is the name of the field that has to be confirmed with "_confirmation" appended. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :email, confirmation: true end ``` @@ -349,7 +349,7 @@ confirmation, make sure to add a presence check for the confirmation attribute (we'll take a look at `presence` later on in this guide): ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :email, confirmation: true validates :email_confirmation, presence: true end @@ -360,7 +360,7 @@ confirmation constraint will be case sensitive or not. This option defaults to true. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :email, confirmation: { case_sensitive: false } end ``` @@ -373,7 +373,7 @@ This helper validates that the attributes' values are not included in a given set. In fact, this set can be any enumerable object. ```ruby -class Account < ActiveRecord::Base +class Account < ApplicationRecord validates :subdomain, exclusion: { in: %w(www us ca jp), message: "%{value} is reserved." } end @@ -393,7 +393,7 @@ This helper validates the attributes' values by testing whether they match a given regular expression, which is specified using the `:with` option. ```ruby -class Product < ActiveRecord::Base +class Product < ApplicationRecord validates :legacy_code, format: { with: /\A[a-zA-Z]+\z/, message: "only allows letters" } end @@ -409,7 +409,7 @@ This helper validates that the attributes' values are included in a given set. In fact, this set can be any enumerable object. ```ruby -class Coffee < ActiveRecord::Base +class Coffee < ApplicationRecord validates :size, inclusion: { in: %w(small medium large), message: "%{value} is not a valid size" } end @@ -428,7 +428,7 @@ This helper validates the length of the attributes' values. It provides a variety of options, so you can specify length constraints in different ways: ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :name, length: { minimum: 2 } validates :bio, length: { maximum: 500 } validates :password, length: { in: 6..20 } @@ -451,7 +451,7 @@ number corresponding to the length constraint being used. You can still use the `:message` option to specify an error message. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :bio, length: { maximum: 1000, too_long: "%{count} characters is the maximum allowed" } end @@ -483,7 +483,7 @@ WARNING. Note that the regular expression above allows a trailing newline character. ```ruby -class Player < ActiveRecord::Base +class Player < ApplicationRecord validates :points, numericality: true validates :games_played, numericality: { only_integer: true } end @@ -521,7 +521,7 @@ This helper validates that the specified attributes are not empty. It uses the is, a string that is either empty or consists of whitespace. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :name, :login, :email, presence: true end ``` @@ -531,7 +531,7 @@ whether the associated object itself is present, and not the foreign key used to map the association. ```ruby -class LineItem < ActiveRecord::Base +class LineItem < ApplicationRecord belongs_to :order validates :order, presence: true end @@ -541,7 +541,7 @@ In order to validate associated records whose presence is required, you must specify the `:inverse_of` option for the association: ```ruby -class Order < ActiveRecord::Base +class Order < ApplicationRecord has_many :line_items, inverse_of: :order end ``` @@ -568,7 +568,7 @@ This helper validates that the specified attributes are absent. It uses the is, a string that is either empty or consists of whitespace. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :name, :login, :email, absence: true end ``` @@ -578,7 +578,7 @@ whether the associated object itself is absent, and not the foreign key used to map the association. ```ruby -class LineItem < ActiveRecord::Base +class LineItem < ApplicationRecord belongs_to :order validates :order, absence: true end @@ -588,7 +588,7 @@ In order to validate associated records whose absence is required, you must specify the `:inverse_of` option for the association: ```ruby -class Order < ActiveRecord::Base +class Order < ApplicationRecord has_many :line_items, inverse_of: :order end ``` @@ -611,7 +611,7 @@ with the same value for a column that you intend to be unique. To avoid that, you must create a unique index on that column in your database. ```ruby -class Account < ActiveRecord::Base +class Account < ApplicationRecord validates :email, uniqueness: true end ``` @@ -623,7 +623,7 @@ There is a `:scope` option that you can use to specify one or more attributes th are used to limit the uniqueness check: ```ruby -class Holiday < ActiveRecord::Base +class Holiday < ApplicationRecord validates :name, uniqueness: { scope: :year, message: "should happen once per year" } end @@ -635,7 +635,7 @@ uniqueness constraint will be case sensitive or not. This option defaults to true. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :name, uniqueness: { case_sensitive: false } end ``` @@ -658,7 +658,7 @@ class GoodnessValidator < ActiveModel::Validator end end -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates_with GoodnessValidator end ``` @@ -686,7 +686,7 @@ class GoodnessValidator < ActiveModel::Validator end end -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates_with GoodnessValidator, fields: [:first_name, :last_name] end ``` @@ -699,7 +699,7 @@ If your validator is complex enough that you want instance variables, you can easily use a plain old Ruby object instead: ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validate do |person| GoodnessValidator.new(person).validate end @@ -728,7 +728,7 @@ passed to `validates_each` will be tested against it. In the following example, we don't want names and surnames to begin with lower case. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates_each :name, :surname do |record, attr, value| record.errors.add(attr, 'must start with upper case') if value =~ /\A[[:lower:]]/ end @@ -751,7 +751,7 @@ The `:allow_nil` option skips the validation when the value being validated is `nil`. ```ruby -class Coffee < ActiveRecord::Base +class Coffee < ApplicationRecord validates :size, inclusion: { in: %w(small medium large), message: "%{value} is not a valid size" }, allow_nil: true end @@ -764,7 +764,7 @@ will let validation pass if the attribute's value is `blank?`, like `nil` or an empty string for example. ```ruby -class Topic < ActiveRecord::Base +class Topic < ApplicationRecord validates :title, length: { is: 5 }, allow_blank: true end @@ -787,7 +787,7 @@ A `Proc` `:message` value is given two arguments: a message key for i18n, and a hash with `:model`, `:attribute`, and `:value` key-value pairs. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord # Hard-coded message validates :name, presence: { message: "must be given please" } @@ -818,7 +818,7 @@ new record is created or `on: :update` to run the validation only when a record is updated. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord # it will be possible to update email with a duplicated value validates :email, uniqueness: true, on: :create @@ -837,7 +837,7 @@ You can also specify validations to be strict and raise `ActiveModel::StrictValidationFailed` when the object is invalid. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :name, presence: { strict: true } end @@ -847,7 +847,7 @@ Person.new.valid? # => ActiveModel::StrictValidationFailed: Name can't be blank There is also the ability to pass a custom exception to the `:strict` option. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :token, presence: true, uniqueness: true, strict: TokenGenerationException end @@ -871,7 +871,7 @@ to the name of a method that will get called right before validation happens. This is the most commonly used option. ```ruby -class Order < ActiveRecord::Base +class Order < ApplicationRecord validates :card_number, presence: true, if: :paid_with_card? def paid_with_card? @@ -887,7 +887,7 @@ contain valid Ruby code. You should use this option only when the string represents a really short condition. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :surname, presence: true, if: "name.nil?" end ``` @@ -900,7 +900,7 @@ inline condition instead of a separate method. This option is best suited for one-liners. ```ruby -class Account < ActiveRecord::Base +class Account < ApplicationRecord validates :password, confirmation: true, unless: Proc.new { |a| a.password.blank? } end @@ -912,7 +912,7 @@ Sometimes it is useful to have multiple validations use one condition. It can be easily achieved using `with_options`. ```ruby -class User < ActiveRecord::Base +class User < ApplicationRecord with_options if: :is_admin? do |admin| admin.validates :password, length: { minimum: 10 } admin.validates :email, presence: true @@ -930,7 +930,7 @@ should happen, an `Array` can be used. Moreover, you can apply both `:if` and `:unless` to the same validation. ```ruby -class Computer < ActiveRecord::Base +class Computer < ApplicationRecord validates :mouse, presence: true, if: ["market.retail?", :desktop?], unless: Proc.new { |c| c.trackpad.present? } @@ -984,7 +984,7 @@ class EmailValidator < ActiveModel::EachValidator end end -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :email, presence: true, email: true end ``` @@ -1008,7 +1008,7 @@ so your custom validation methods should add errors to it when you wish validation to fail: ```ruby -class Invoice < ActiveRecord::Base +class Invoice < ApplicationRecord validate :expiration_date_cannot_be_in_the_past, :discount_cannot_be_greater_than_total_value @@ -1032,7 +1032,7 @@ custom validations by giving an `:on` option to the `validate` method, with either: `:create` or `:update`. ```ruby -class Invoice < ActiveRecord::Base +class Invoice < ApplicationRecord validate :active_customer, on: :create def active_customer @@ -1053,7 +1053,7 @@ The following is a list of the most commonly used methods. Please refer to the ` Returns an instance of the class `ActiveModel::Errors` containing all errors. Each key is the attribute name and the value is an array of strings with all errors. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :name, presence: true, length: { minimum: 3 } end @@ -1072,7 +1072,7 @@ person.errors.messages # => {} `errors[]` is used when you want to check the error messages for a specific attribute. It returns an array of strings with all error messages for the given attribute, each string with one error message. If there are no errors related to the attribute, it returns an empty array. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :name, presence: true, length: { minimum: 3 } end @@ -1097,7 +1097,7 @@ The `add` method lets you add an error message related to a particular attribute The `errors.full_messages` method (or its equivalent, `errors.to_a`) returns the error messages in a user-friendly format, with the capitalized attribute name prepended to each message, as shown in the examples below. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord def a_method_used_for_validation_purposes errors.add(:name, "cannot contain the characters !@#%*()_-+=") end @@ -1115,7 +1115,7 @@ person.errors.full_messages An equivalent to `errors#add` is to use `<<` to append a message to the `errors.messages` array for an attribute: ```ruby - class Person < ActiveRecord::Base + class Person < ApplicationRecord def a_method_used_for_validation_purposes errors.messages[:name] << "cannot contain the characters !@#%*()_-+=" end @@ -1136,7 +1136,7 @@ You can specify a validator type to the returned error details hash using the `errors.add` method. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord def a_method_used_for_validation_purposes errors.add(:name, :invalid_characters) end @@ -1152,7 +1152,7 @@ To improve the error details to contain the unallowed characters set for instanc you can pass additional keys to `errors.add`. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord def a_method_used_for_validation_purposes errors.add(:name, :invalid_characters, not_allowed: "!@#%*()_-+=") end @@ -1172,7 +1172,7 @@ validator type. You can add error messages that are related to the object's state as a whole, instead of being related to a specific attribute. You can use this method when you want to say that the object is invalid, no matter the values of its attributes. Since `errors[:base]` is an array, you can simply add a string to it and it will be used as an error message. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord def a_method_used_for_validation_purposes errors[:base] << "This person is invalid because ..." end @@ -1184,7 +1184,7 @@ end The `clear` method is used when you intentionally want to clear all the messages in the `errors` collection. Of course, calling `errors.clear` upon an invalid object won't actually make it valid: the `errors` collection will now be empty, but the next time you call `valid?` or any method that tries to save this object to the database, the validations will run again. If any of the validations fail, the `errors` collection will be filled again. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :name, presence: true, length: { minimum: 3 } end @@ -1207,7 +1207,7 @@ p.errors[:name] The `size` method returns the total number of error messages for the object. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :name, presence: true, length: { minimum: 3 } 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/association_basics.md b/guides/source/association_basics.md index c272daac28..c3bac320eb 100644 --- a/guides/source/association_basics.md +++ b/guides/source/association_basics.md @@ -101,7 +101,7 @@ NOTE: `belongs_to` associations _must_ use the singular term. If you used the pl The corresponding migration might look like this: ```ruby -class CreateOrders < ActiveRecord::Migration +class CreateOrders < ActiveRecord::Migration[5.0] def change create_table :customers do |t| t.string :name @@ -132,7 +132,7 @@ end The corresponding migration might look like this: ```ruby -class CreateSuppliers < ActiveRecord::Migration +class CreateSuppliers < ActiveRecord::Migration[5.0] def change create_table :suppliers do |t| t.string :name @@ -176,7 +176,7 @@ NOTE: The name of the other model is pluralized when declaring a `has_many` asso The corresponding migration might look like this: ```ruby -class CreateCustomers < ActiveRecord::Migration +class CreateCustomers < ActiveRecord::Migration[5.0] def change create_table :customers do |t| t.string :name @@ -218,7 +218,7 @@ end The corresponding migration might look like this: ```ruby -class CreateAppointments < ActiveRecord::Migration +class CreateAppointments < ActiveRecord::Migration[5.0] def change create_table :physicians do |t| t.string :name @@ -304,7 +304,7 @@ end The corresponding migration might look like this: ```ruby -class CreateAccountHistories < ActiveRecord::Migration +class CreateAccountHistories < ActiveRecord::Migration[5.0] def change create_table :suppliers do |t| t.string :name @@ -345,7 +345,7 @@ end The corresponding migration might look like this: ```ruby -class CreateAssembliesAndParts < ActiveRecord::Migration +class CreateAssembliesAndParts < ActiveRecord::Migration[5.0] def change create_table :assemblies do |t| t.string :name @@ -384,7 +384,7 @@ end The corresponding migration might look like this: ```ruby -class CreateSuppliers < ActiveRecord::Migration +class CreateSuppliers < ActiveRecord::Migration[5.0] def change create_table :suppliers do |t| t.string :name @@ -466,7 +466,7 @@ Similarly, you can retrieve `@product.pictures`. If you have an instance of the `Picture` model, you can get to its parent via `@picture.imageable`. To make this work, you need to declare both a foreign key column and a type column in the model that declares the polymorphic interface: ```ruby -class CreatePictures < ActiveRecord::Migration +class CreatePictures < ActiveRecord::Migration[5.0] def change create_table :pictures do |t| t.string :name @@ -483,7 +483,7 @@ end This migration can be simplified by using the `t.references` form: ```ruby -class CreatePictures < ActiveRecord::Migration +class CreatePictures < ActiveRecord::Migration[5.0] def change create_table :pictures do |t| t.string :name @@ -514,7 +514,7 @@ With this setup, you can retrieve `@employee.subordinates` and `@employee.manage In your migrations/schema, you will add a references column to the model itself. ```ruby -class CreateEmployees < ActiveRecord::Migration +class CreateEmployees < ActiveRecord::Migration[5.0] def change create_table :employees do |t| t.references :manager, index: true @@ -575,7 +575,7 @@ end This declaration needs to be backed up by the proper foreign key declaration on the orders table: ```ruby -class CreateOrders < ActiveRecord::Migration +class CreateOrders < ActiveRecord::Migration[5.0] def change create_table :orders do |t| t.datetime :order_date @@ -611,7 +611,7 @@ end These need to be backed up by a migration to create the `assemblies_parts` table. This table should be created without a primary key: ```ruby -class CreateAssembliesPartsJoinTable < ActiveRecord::Migration +class CreateAssembliesPartsJoinTable < ActiveRecord::Migration[5.0] def change create_table :assemblies_parts, id: false do |t| t.integer :assembly_id @@ -629,7 +629,7 @@ We pass `id: false` to `create_table` because that table does not represent a mo You can also use the method `create_join_table` ```ruby -class CreateAssembliesPartsJoinTable < ActiveRecord::Migration +class CreateAssembliesPartsJoinTable < ActiveRecord::Migration[5.0] def change create_join_table :assemblies, :parts do |t| t.index :assembly_id 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..8382bde4d3 100644 --- a/guides/source/engines.md +++ b/guides/source/engines.md @@ -508,7 +508,7 @@ Turning the model into this: ```ruby module Blorgh - class Article < ActiveRecord::Base + class Article < ApplicationRecord has_many :comments end end @@ -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 @@ -1129,7 +1129,7 @@ end ```ruby # Blorgh/app/models/article.rb -class Article < ActiveRecord::Base +class Article < ApplicationRecord has_many :comments end ``` @@ -1150,7 +1150,7 @@ end ```ruby # Blorgh/app/models/article.rb -class Article < ActiveRecord::Base +class Article < ApplicationRecord has_many :comments def summary "#{title}" @@ -1171,7 +1171,7 @@ classes at run time allowing you to significantly modularize your code. ```ruby # MyApp/app/models/blorgh/article.rb -class Blorgh::Article < ActiveRecord::Base +class Blorgh::Article < ApplicationRecord include Blorgh::Concerns::Models::Article def time_since_created @@ -1187,7 +1187,7 @@ end ```ruby # Blorgh/app/models/article.rb -class Article < ActiveRecord::Base +class Article < ApplicationRecord include Blorgh::Concerns::Models::Article end ``` diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md index 5700e71103..1416d00de5 100644 --- a/guides/source/getting_started.md +++ b/guides/source/getting_started.md @@ -679,7 +679,7 @@ If you look in the `db/migrate/YYYYMMDDHHMMSS_create_articles.rb` file (remember, yours will have a slightly different name), here's what you'll find: ```ruby -class CreateArticles < ActiveRecord::Migration +class CreateArticles < ActiveRecord::Migration[5.0] def change create_table :articles do |t| t.string :title @@ -1542,7 +1542,7 @@ In addition to the model, Rails has also made a migration to create the corresponding database table: ```ruby -class CreateComments < ActiveRecord::Migration +class CreateComments < ActiveRecord::Migration[5.0] def change create_table :comments do |t| t.string :commenter 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/plugins.md b/guides/source/plugins.md index 922bbb4f73..ae8c30515a 100644 --- a/guides/source/plugins.md +++ b/guides/source/plugins.md @@ -17,7 +17,7 @@ After reading this guide, you will know: This guide describes how to build a test-driven plugin that will: * Extend core Ruby classes like Hash and String. -* Add methods to `ActiveRecord::Base` in the tradition of the `acts_as` plugins. +* Add methods to `ApplicationRecord` in the tradition of the `acts_as` plugins. * Give you information about where to put generators in your plugin. For the purpose of this guide pretend for a moment that you are an avid bird watcher. @@ -182,7 +182,6 @@ To start out, write a failing test that shows the behavior you'd like: require 'test_helper' class ActsAsYaffleTest < ActiveSupport::TestCase - def test_a_hickwalls_yaffle_text_field_should_be_last_squawk assert_equal "last_squawk", Hickwall.yaffle_text_field end @@ -190,7 +189,6 @@ class ActsAsYaffleTest < ActiveSupport::TestCase def test_a_wickwalls_yaffle_text_field_should_be_last_tweet assert_equal "last_tweet", Wickwall.yaffle_text_field end - end ``` @@ -234,22 +232,22 @@ like yaffles. ```ruby # test/dummy/app/models/hickwall.rb -class Hickwall < ActiveRecord::Base +class Hickwall < ApplicationRecord acts_as_yaffle end # test/dummy/app/models/wickwall.rb -class Wickwall < ActiveRecord::Base +class Wickwall < ApplicationRecord acts_as_yaffle yaffle_text_field: :last_tweet end - ``` We will also add code to define the `acts_as_yaffle` method. ```ruby # yaffle/lib/yaffle/acts_as_yaffle.rb + module Yaffle module ActsAsYaffle extend ActiveSupport::Concern @@ -265,7 +263,13 @@ module Yaffle end end -ActiveRecord::Base.include(Yaffle::ActsAsYaffle) +# test/dummy/app/models/application_record.rb + +class ApplicationRecord < ActiveRecord::Base + include Yaffle::ActsAsYaffle + + self.abstract_class = true +end ``` You can then return to the root directory (`cd ../..`) of your plugin and rerun the tests using `rake`. @@ -308,7 +312,13 @@ module Yaffle end end -ActiveRecord::Base.include(Yaffle::ActsAsYaffle) +# test/dummy/app/models/application_record.rb + +class ApplicationRecord < ActiveRecord::Base + include Yaffle::ActsAsYaffle + + self.abstract_class = true +end ``` When you run `rake`, you should see the tests all pass: @@ -329,7 +339,6 @@ To start out, write a failing test that shows the behavior you'd like: require 'test_helper' class ActsAsYaffleTest < ActiveSupport::TestCase - def test_a_hickwalls_yaffle_text_field_should_be_last_squawk assert_equal "last_squawk", Hickwall.yaffle_text_field end @@ -382,7 +391,13 @@ module Yaffle end end -ActiveRecord::Base.include(Yaffle::ActsAsYaffle) +# test/dummy/app/models/application_record.rb + +class ApplicationRecord < ActiveRecord::Base + include Yaffle::ActsAsYaffle + + self.abstract_class = true +end ``` Run `rake` one final time and you should see: diff --git a/guides/source/security.md b/guides/source/security.md index df8c24864e..b301736c36 100644 --- a/guides/source/security.md +++ b/guides/source/security.md @@ -381,9 +381,9 @@ Refer to the Injection section for countermeasures against XSS. It is _recommend **CSRF** Cross-Site Request Forgery (CSRF), also known as Cross-Site Reference Forgery (XSRF), is a gigantic attack method, it allows the attacker to do everything the administrator or Intranet user may do. As you have already seen above how CSRF works, here are a few examples of what attackers can do in the Intranet or admin interface. -A real-world example is a [router reconfiguration by CSRF](http://www.h-online.com/security/news/item/Symantec-reports-first-active-attack-on-a-DSL-router-735883.html). The attackers sent a malicious e-mail, with CSRF in it, to Mexican users. The e-mail claimed there was an e-card waiting for them, but it also contained an image tag that resulted in a HTTP-GET request to reconfigure the user's router (which is a popular model in Mexico). The request changed the DNS-settings so that requests to a Mexico-based banking site would be mapped to the attacker's site. Everyone who accessed the banking site through that router saw the attacker's fake web site and had their credentials stolen. +A real-world example is a [router reconfiguration by CSRF](http://www.h-online.com/security/news/item/Symantec-reports-first-active-attack-on-a-DSL-router-735883.html). The attackers sent a malicious e-mail, with CSRF in it, to Mexican users. The e-mail claimed there was an e-card waiting for the user, but it also contained an image tag that resulted in a HTTP-GET request to reconfigure the user's router (which is a popular model in Mexico). The request changed the DNS-settings so that requests to a Mexico-based banking site would be mapped to the attacker's site. Everyone who accessed the banking site through that router saw the attacker's fake web site and had their credentials stolen. -Another example changed Google Adsense's e-mail address and password by. If the victim was logged into Google Adsense, the administration interface for Google advertisements campaigns, an attacker could change their credentials.
 +Another example changed Google Adsense's e-mail address and password. If the victim was logged into Google Adsense, the administration interface for Google advertisement campaigns, an attacker could change the credentials of the victim.
 Another popular attack is to spam your web application, your blog or forum to propagate malicious XSS. Of course, the attacker has to know the URL structure, but most Rails URLs are quite straightforward or they will be easy to find out, if it is an open-source application's admin interface. The attacker may even do 1,000 lucky guesses by just including malicious IMG-tags which try every possible combination. 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/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md index fa6a01671b..9ba5021c4a 100644 --- a/guides/source/upgrading_ruby_on_rails.md +++ b/guides/source/upgrading_ruby_on_rails.md @@ -53,7 +53,7 @@ Don't forget to review the difference, to see if there were any unexpected chang Upgrading from Rails 4.2 to Rails 5.0 ------------------------------------- -### Halting callback chains by returning `false` +### Halting callback chains via `throw(:abort)` In Rails 4.2, when a 'before' callback returns `false` in Active Record and Active Model, then the entire callback chain is halted. In other words, 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/app_base.rb b/railties/lib/rails/generators/app_base.rb index 56c9d3e354..c243b0c60e 100644 --- a/railties/lib/rails/generators/app_base.rb +++ b/railties/lib/rails/generators/app_base.rb @@ -278,10 +278,8 @@ module Rails end def jbuilder_gemfile_entry - return [] if options[:api] - comment = 'Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder' - GemfileEntry.version('jbuilder', '~> 2.0', comment) + GemfileEntry.new 'jbuilder', '~> 2.0', comment, {}, options[:api] end def coffee_gemfile_entry diff --git a/railties/lib/rails/generators/named_base.rb b/railties/lib/rails/generators/named_base.rb index 243694f38e..658d883883 100644 --- a/railties/lib/rails/generators/named_base.rb +++ b/railties/lib/rails/generators/named_base.rb @@ -129,6 +129,18 @@ module Rails uncountable? ? "#{plural_table_name}_index" : plural_table_name end + def show_helper + "#{singular_table_name}_url(@#{singular_table_name})" + end + + def edit_helper + "edit_#{show_helper}" + end + + def new_helper + "new_#{singular_table_name}_url" + end + def singular_table_name @singular_table_name ||= (pluralize_table_names? ? table_name.singularize : table_name) end diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb index 889154494d..9997cf3b35 100644 --- a/railties/lib/rails/generators/rails/app/app_generator.rb +++ b/railties/lib/rails/generators/rails/app/app_generator.rb @@ -293,6 +293,12 @@ module Rails end end + def delete_application_record_skipping_active_record + if options[:skip_active_record] + remove_file 'app/models/application_record.rb' + end + end + def delete_active_record_initializers_skipping_active_record if options[:skip_active_record] remove_file 'config/initializers/active_record_belongs_to_required_by_default.rb' diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile b/railties/lib/rails/generators/rails/app/templates/Gemfile index a0880d5166..4384d9b6eb 100644 --- a/railties/lib/rails/generators/rails/app/templates/Gemfile +++ b/railties/lib/rails/generators/rails/app/templates/Gemfile @@ -22,9 +22,6 @@ source 'https://rubygems.org' # gem 'capistrano-rails', group: :development <%- if options.api? -%> -# Use ActiveModelSerializers to serialize JSON responses -gem 'active_model_serializers', '~> 0.10.0.rc2' - # Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible # gem 'rack-cors' @@ -41,17 +38,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/app/models/application_record.rb b/railties/lib/rails/generators/rails/app/templates/app/models/application_record.rb new file mode 100644 index 0000000000..10a4cba84d --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/app/models/application_record.rb @@ -0,0 +1,3 @@ +class ApplicationRecord < ActiveRecord::Base + self.abstract_class = true +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/plugin/templates/app/models/application_record.rb.tt b/railties/lib/rails/generators/rails/plugin/templates/app/models/application_record.rb.tt new file mode 100644 index 0000000000..8aa3de78f1 --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/app/models/application_record.rb.tt @@ -0,0 +1,6 @@ +<%= wrap_in_modules <<-rb.strip_heredoc + class ApplicationRecord < ActiveRecord::Base + self.abstract_class = true + end +rb +%> 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/lib/rails/generators/test_unit/controller/templates/functional_test.rb b/railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb index 5a8a3ca5e0..4f2ceb8589 100644 --- a/railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb +++ b/railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb @@ -1,11 +1,9 @@ require 'test_helper' <% module_namespacing do -%> -class <%= class_name %>ControllerTest < ActionController::TestCase +class <%= class_name %>ControllerTest < ActionDispatch::IntegrationTest <% if mountable_engine? -%> - setup do - @routes = Engine.routes - end + include Engine.routes.url_helpers <% end -%> <% if actions.empty? -%> @@ -15,7 +13,7 @@ class <%= class_name %>ControllerTest < ActionController::TestCase <% else -%> <% actions.each do |action| -%> test "should get <%= action %>" do - get :<%= action %> + get <%= file_name %>_<%= action %>_url assert_response :success end diff --git a/railties/lib/rails/generators/test_unit/scaffold/templates/api_functional_test.rb b/railties/lib/rails/generators/test_unit/scaffold/templates/api_functional_test.rb index f302cd6c3d..de5814eae9 100644 --- a/railties/lib/rails/generators/test_unit/scaffold/templates/api_functional_test.rb +++ b/railties/lib/rails/generators/test_unit/scaffold/templates/api_functional_test.rb @@ -1,40 +1,41 @@ require 'test_helper' <% module_namespacing do -%> -class <%= controller_class_name %>ControllerTest < ActionController::TestCase +class <%= controller_class_name %>ControllerTest < ActionDispatch::IntegrationTest + <% if mountable_engine? -%> + include Engine.routes.url_helpers + + <% end -%> setup do @<%= singular_table_name %> = <%= fixture_name %>(:one) -<% if mountable_engine? -%> - @routes = Engine.routes -<% end -%> end test "should get index" do - get :index + get <%= index_helper %>_url assert_response :success end test "should create <%= singular_table_name %>" do assert_difference('<%= class_name %>.count') do - post :create, params: { <%= "#{singular_table_name}: { #{attributes_hash} }" %> } + post <%= index_helper %>_url, params: { <%= "#{singular_table_name}: { #{attributes_hash} }" %> } end assert_response 201 end test "should show <%= singular_table_name %>" do - get :show, params: { id: <%= "@#{singular_table_name}" %> } + get <%= show_helper %> assert_response :success end test "should update <%= singular_table_name %>" do - patch :update, params: { id: <%= "@#{singular_table_name}" %>, <%= "#{singular_table_name}: { #{attributes_hash} }" %> } + patch <%= show_helper %>, params: { <%= "#{singular_table_name}: { #{attributes_hash} }" %> } assert_response 200 end test "should destroy <%= singular_table_name %>" do assert_difference('<%= class_name %>.count', -1) do - delete :destroy, params: { id: <%= "@#{singular_table_name}" %> } + delete <%= show_helper %> end assert_response 204 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 50b98b2631..1989d84c79 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 @@ -1,50 +1,51 @@ require 'test_helper' <% module_namespacing do -%> -class <%= controller_class_name %>ControllerTest < ActionController::TestCase +class <%= controller_class_name %>ControllerTest < ActionDispatch::IntegrationTest + <%- if mountable_engine? -%> + include Engine.routes.url_helpers + <% end -%> + setup do @<%= singular_table_name %> = <%= fixture_name %>(:one) -<% if mountable_engine? -%> - @routes = Engine.routes -<% end -%> end test "should get index" do - get :index + get <%= index_helper %>_url assert_response :success end test "should get new" do - get :new + get <%= new_helper %> assert_response :success end test "should create <%= singular_table_name %>" do assert_difference('<%= class_name %>.count') do - post :create, params: { <%= "#{singular_table_name}: { #{attributes_hash} }" %> } + post <%= index_helper %>_url, params: { <%= "#{singular_table_name}: { #{attributes_hash} }" %> } end assert_redirected_to <%= singular_table_name %>_path(<%= class_name %>.last) end test "should show <%= singular_table_name %>" do - get :show, params: { id: <%= "@#{singular_table_name}" %> } + get <%= show_helper %> assert_response :success end test "should get edit" do - get :edit, params: { id: <%= "@#{singular_table_name}" %> } + get <%= edit_helper %> assert_response :success end test "should update <%= singular_table_name %>" do - patch :update, params: { id: <%= "@#{singular_table_name}" %>, <%= "#{singular_table_name}: { #{attributes_hash} }" %> } + patch <%= show_helper %>, params: { <%= "#{singular_table_name}: { #{attributes_hash} }" %> } assert_redirected_to <%= singular_table_name %>_path(<%= "@#{singular_table_name}" %>) end test "should destroy <%= singular_table_name %>" do assert_difference('<%= class_name %>.count', -1) do - delete :destroy, params: { id: <%= "@#{singular_table_name}" %> } + delete <%= show_helper %> end assert_redirected_to <%= index_helper %>_path diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index 49f63d5d31..87e28288ee 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -103,7 +103,7 @@ module ApplicationTests RUBY app_file 'db/migrate/20140708012246_create_user.rb', <<-RUBY - class CreateUser < ActiveRecord::Migration + class CreateUser < ActiveRecord::Migration::Current def change create_table :users end @@ -228,6 +228,7 @@ module ApplicationTests end test "the application can be eager loaded even when there are no frameworks" do + FileUtils.rm_rf("#{app_path}/app/models/application_record.rb") FileUtils.rm_rf("#{app_path}/config/environments") add_to_config <<-RUBY config.eager_load = true diff --git a/railties/test/application/loading_test.rb b/railties/test/application/loading_test.rb index 2106708c98..2cc599ca6f 100644 --- a/railties/test/application/loading_test.rb +++ b/railties/test/application/loading_test.rb @@ -290,7 +290,7 @@ class LoadingTest < ActiveSupport::TestCase extend Rack::Test::Methods app_file "db/migrate/1_create_posts.rb", <<-MIGRATION - class CreatePosts < ActiveRecord::Migration + class CreatePosts < ActiveRecord::Migration::Current def change create_table :posts do |t| t.string :title, default: "TITLE" @@ -306,7 +306,7 @@ class LoadingTest < ActiveSupport::TestCase assert_equal "TITLE", last_response.body app_file "db/migrate/2_add_body_to_posts.rb", <<-MIGRATION - class AddBodyToPosts < ActiveRecord::Migration + class AddBodyToPosts < ActiveRecord::Migration::Current def change add_column :posts, :body, :text, default: "BODY" end diff --git a/railties/test/application/rake/migrations_test.rb b/railties/test/application/rake/migrations_test.rb index 6b74707959..580ed269cb 100644 --- a/railties/test/application/rake/migrations_test.rb +++ b/railties/test/application/rake/migrations_test.rb @@ -18,7 +18,7 @@ module ApplicationTests `bin/rails generate model user username:string password:string` app_file "db/migrate/01_a_migration.bukkits.rb", <<-MIGRATION - class AMigration < ActiveRecord::Migration + class AMigration < ActiveRecord::Migration::Current end MIGRATION @@ -158,12 +158,12 @@ module ApplicationTests Dir.chdir(app_path) do app_file "db/migrate/1_one_migration.rb", <<-MIGRATION - class OneMigration < ActiveRecord::Migration + class OneMigration < ActiveRecord::Migration::Current end MIGRATION app_file "db/migrate/02_two_migration.rb", <<-MIGRATION - class TwoMigration < ActiveRecord::Migration + class TwoMigration < ActiveRecord::Migration::Current end MIGRATION diff --git a/railties/test/application/rake_test.rb b/railties/test/application/rake_test.rb index 0da0928b48..6373c7b42b 100644 --- a/railties/test/application/rake_test.rb +++ b/railties/test/application/rake_test.rb @@ -98,7 +98,7 @@ module ApplicationTests end def test_code_statistics_sanity - assert_match "Code LOC: 7 Test LOC: 0 Code to Test Ratio: 1:0.0", + assert_match "Code LOC: 10 Test LOC: 0 Code to Test Ratio: 1:0.0", Dir.chdir(app_path){ `bin/rake stats` } end @@ -186,7 +186,7 @@ module ApplicationTests def test_scaffold_tests_pass_by_default output = Dir.chdir(app_path) do `bin/rails generate scaffold user username:string password:string; - bin/rake db:migrate test` + RAILS_ENV=test bin/rake db:migrate test` end assert_match(/7 runs, 12 assertions, 0 failures, 0 errors/, output) @@ -205,7 +205,7 @@ module ApplicationTests output = Dir.chdir(app_path) do `bin/rails generate scaffold user username:string password:string; - bin/rake db:migrate test` + RAILS_ENV=test bin/rake db:migrate test` end assert_match(/5 runs, 7 assertions, 0 failures, 0 errors/, output) @@ -218,7 +218,7 @@ module ApplicationTests output = Dir.chdir(app_path) do `bin/rails generate scaffold LineItems product:references cart:belongs_to; - bin/rake db:migrate test` + RAILS_ENV=test bin/rake db:migrate test` end assert_match(/7 runs, 12 assertions, 0 failures, 0 errors/, output) diff --git a/railties/test/generators/api_app_generator_test.rb b/railties/test/generators/api_app_generator_test.rb index be2cd3a853..2c24a6e46a 100644 --- a/railties/test/generators/api_app_generator_test.rb +++ b/railties/test/generators/api_app_generator_test.rb @@ -37,9 +37,8 @@ class ApiAppGeneratorTest < Rails::Generators::TestCase assert_no_match(/gem 'coffee-rails'/, content) assert_no_match(/gem 'jquery-rails'/, content) assert_no_match(/gem 'sass-rails'/, content) - assert_no_match(/gem 'jbuilder'/, content) assert_no_match(/gem 'web-console'/, content) - assert_match(/gem 'active_model_serializers'/, content) + assert_match(/# gem 'jbuilder'/, content) end assert_file "config/application.rb" do |content| diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index 446fef562b..3f2ac3d2b6 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -334,6 +334,7 @@ class AppGeneratorTest < Rails::Generators::TestCase run_generator [destination_root, "--skip-active-record"] assert_no_file "config/database.yml" assert_no_file "config/initializers/active_record_belongs_to_required_by_default.rb" + assert_no_file "app/models/application_record.rb" assert_file "config/application.rb", /#\s+require\s+["']active_record\/railtie["']/ assert_file "test/test_helper.rb" do |helper_content| assert_no_match(/fixtures :all/, helper_content) @@ -505,7 +506,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 +515,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/migration_generator_test.rb b/railties/test/generators/migration_generator_test.rb index 199743a396..80f284674d 100644 --- a/railties/test/generators/migration_generator_test.rb +++ b/railties/test/generators/migration_generator_test.rb @@ -7,7 +7,7 @@ class MigrationGeneratorTest < Rails::Generators::TestCase def test_migration migration = "change_title_body_from_posts" run_generator [migration] - assert_migration "db/migrate/#{migration}.rb", /class ChangeTitleBodyFromPosts < ActiveRecord::Migration/ + assert_migration "db/migrate/#{migration}.rb", /class ChangeTitleBodyFromPosts < ActiveRecord::Migration\[[0-9.]+\]/ end def test_migrations_generated_simultaneously @@ -26,7 +26,7 @@ class MigrationGeneratorTest < Rails::Generators::TestCase def test_migration_with_class_name migration = "ChangeTitleBodyFromPosts" run_generator [migration] - assert_migration "db/migrate/change_title_body_from_posts.rb", /class #{migration} < ActiveRecord::Migration/ + assert_migration "db/migrate/change_title_body_from_posts.rb", /class #{migration} < ActiveRecord::Migration\[[0-9.]+\]/ end def test_migration_with_invalid_file_name diff --git a/railties/test/generators/model_generator_test.rb b/railties/test/generators/model_generator_test.rb index 64b9a480f3..fb502ec0c5 100644 --- a/railties/test/generators/model_generator_test.rb +++ b/railties/test/generators/model_generator_test.rb @@ -35,6 +35,17 @@ class ModelGeneratorTest < Rails::Generators::TestCase assert_no_migration "db/migrate/create_accounts.rb" end + def test_model_with_existent_application_record + mkdir_p "#{destination_root}/app/models" + touch "#{destination_root}/app/models/application_record.rb" + + Dir.chdir(destination_root) do + run_generator ["account"] + end + + assert_file "app/models/account.rb", /class Account < ApplicationRecord/ + end + def test_plural_names_are_singularized content = run_generator ["accounts".freeze] assert_file "app/models/account.rb", /class Account < ActiveRecord::Base/ @@ -57,12 +68,12 @@ class ModelGeneratorTest < Rails::Generators::TestCase def test_migration run_generator - assert_migration "db/migrate/create_accounts.rb", /class CreateAccounts < ActiveRecord::Migration/ + assert_migration "db/migrate/create_accounts.rb", /class CreateAccounts < ActiveRecord::Migration\[[0-9.]+\]/ end def test_migration_with_namespace run_generator ["Gallery::Image"] - assert_migration "db/migrate/create_gallery_images", /class CreateGalleryImages < ActiveRecord::Migration/ + assert_migration "db/migrate/create_gallery_images", /class CreateGalleryImages < ActiveRecord::Migration\[[0-9.]+\]/ assert_no_migration "db/migrate/create_images" end @@ -70,7 +81,7 @@ class ModelGeneratorTest < Rails::Generators::TestCase run_generator ["Admin::Gallery::Image"] assert_no_migration "db/migrate/create_images" assert_no_migration "db/migrate/create_gallery_images" - assert_migration "db/migrate/create_admin_gallery_images", /class CreateAdminGalleryImages < ActiveRecord::Migration/ + assert_migration "db/migrate/create_admin_gallery_images", /class CreateAdminGalleryImages < ActiveRecord::Migration\[[0-9.]+\]/ assert_migration "db/migrate/create_admin_gallery_images", /create_table :admin_gallery_images/ end @@ -80,7 +91,7 @@ class ModelGeneratorTest < Rails::Generators::TestCase assert_no_migration "db/migrate/create_images" assert_no_migration "db/migrate/create_gallery_images" assert_no_migration "db/migrate/create_admin_gallery_images" - assert_migration "db/migrate/create_admin_gallery_image", /class CreateAdminGalleryImage < ActiveRecord::Migration/ + assert_migration "db/migrate/create_admin_gallery_image", /class CreateAdminGalleryImage < ActiveRecord::Migration\[[0-9.]+\]/ assert_migration "db/migrate/create_admin_gallery_image", /create_table :admin_gallery_image/ ensure ActiveRecord::Base.pluralize_table_names = true @@ -89,7 +100,7 @@ class ModelGeneratorTest < Rails::Generators::TestCase def test_migration_with_namespaces_in_model_name_without_plurization ActiveRecord::Base.pluralize_table_names = false run_generator ["Gallery::Image"] - assert_migration "db/migrate/create_gallery_image", /class CreateGalleryImage < ActiveRecord::Migration/ + assert_migration "db/migrate/create_gallery_image", /class CreateGalleryImage < ActiveRecord::Migration\[[0-9.]+\]/ assert_no_migration "db/migrate/create_gallery_images" ensure ActiveRecord::Base.pluralize_table_names = true @@ -98,7 +109,7 @@ class ModelGeneratorTest < Rails::Generators::TestCase def test_migration_without_pluralization ActiveRecord::Base.pluralize_table_names = false run_generator - assert_migration "db/migrate/create_account", /class CreateAccount < ActiveRecord::Migration/ + assert_migration "db/migrate/create_account", /class CreateAccount < ActiveRecord::Migration\[[0-9.]+\]/ assert_no_migration "db/migrate/create_accounts" ensure ActiveRecord::Base.pluralize_table_names = true @@ -193,10 +204,10 @@ class ModelGeneratorTest < Rails::Generators::TestCase def test_migration_without_timestamps ActiveRecord::Base.timestamped_migrations = false run_generator ["account"] - assert_file "db/migrate/001_create_accounts.rb", /class CreateAccounts < ActiveRecord::Migration/ + assert_file "db/migrate/001_create_accounts.rb", /class CreateAccounts < ActiveRecord::Migration\[[0-9.]+\]/ run_generator ["project"] - assert_file "db/migrate/002_create_projects.rb", /class CreateProjects < ActiveRecord::Migration/ + assert_file "db/migrate/002_create_projects.rb", /class CreateProjects < ActiveRecord::Migration\[[0-9.]+\]/ ensure ActiveRecord::Base.timestamped_migrations = true end diff --git a/railties/test/generators/namespaced_generators_test.rb b/railties/test/generators/namespaced_generators_test.rb index 590f06e19a..d76759a7d1 100644 --- a/railties/test/generators/namespaced_generators_test.rb +++ b/railties/test/generators/namespaced_generators_test.rb @@ -104,12 +104,12 @@ class NamespacedModelGeneratorTest < NamespacedGeneratorTestCase def test_migration run_generator - assert_migration "db/migrate/create_test_app_accounts.rb", /create_table :test_app_accounts/, /class CreateTestAppAccounts < ActiveRecord::Migration/ + assert_migration "db/migrate/create_test_app_accounts.rb", /create_table :test_app_accounts/, /class CreateTestAppAccounts < ActiveRecord::Migration\[[0-9.]+\]/ end def test_migration_with_namespace run_generator ["Gallery::Image"] - assert_migration "db/migrate/create_test_app_gallery_images", /class CreateTestAppGalleryImages < ActiveRecord::Migration/ + assert_migration "db/migrate/create_test_app_gallery_images", /class CreateTestAppGalleryImages < ActiveRecord::Migration\[[0-9.]+\]/ assert_no_migration "db/migrate/create_test_app_images" end @@ -117,7 +117,7 @@ class NamespacedModelGeneratorTest < NamespacedGeneratorTestCase run_generator ["Admin::Gallery::Image"] assert_no_migration "db/migrate/create_images" assert_no_migration "db/migrate/create_gallery_images" - assert_migration "db/migrate/create_test_app_admin_gallery_images", /class CreateTestAppAdminGalleryImages < ActiveRecord::Migration/ + assert_migration "db/migrate/create_test_app_admin_gallery_images", /class CreateTestAppAdminGalleryImages < ActiveRecord::Migration\[[0-9.]+\]/ assert_migration "db/migrate/create_test_app_admin_gallery_images", /create_table :test_app_admin_gallery_images/ end @@ -127,7 +127,7 @@ class NamespacedModelGeneratorTest < NamespacedGeneratorTestCase assert_no_migration "db/migrate/create_images" assert_no_migration "db/migrate/create_gallery_images" assert_no_migration "db/migrate/create_test_app_admin_gallery_images" - assert_migration "db/migrate/create_test_app_admin_gallery_image", /class CreateTestAppAdminGalleryImage < ActiveRecord::Migration/ + assert_migration "db/migrate/create_test_app_admin_gallery_image", /class CreateTestAppAdminGalleryImage < ActiveRecord::Migration\[[0-9.]+\]/ assert_migration "db/migrate/create_test_app_admin_gallery_image", /create_table :test_app_admin_gallery_image/ ensure ActiveRecord::Base.pluralize_table_names = true @@ -218,7 +218,7 @@ class NamespacedScaffoldGeneratorTest < NamespacedGeneratorTestCase /class ProductLinesController < ApplicationController/ assert_file "test/controllers/test_app/product_lines_controller_test.rb", - /module TestApp\n class ProductLinesControllerTest < ActionController::TestCase/ + /module TestApp\n class ProductLinesControllerTest < ActionDispatch::IntegrationTest/ # Views %w(index edit new show _form).each do |view| @@ -285,7 +285,7 @@ class NamespacedScaffoldGeneratorTest < NamespacedGeneratorTestCase end assert_file "test/controllers/test_app/admin/roles_controller_test.rb", - /module TestApp\n class Admin::RolesControllerTest < ActionController::TestCase/ + /module TestApp\n class Admin::RolesControllerTest < ActionDispatch::IntegrationTest/ # Views %w(index edit new show _form).each do |view| @@ -352,7 +352,7 @@ class NamespacedScaffoldGeneratorTest < NamespacedGeneratorTestCase end assert_file "test/controllers/test_app/admin/user/special/roles_controller_test.rb", - /module TestApp\n class Admin::User::Special::RolesControllerTest < ActionController::TestCase/ + /module TestApp\n class Admin::User::Special::RolesControllerTest < ActionDispatch::IntegrationTest/ # Views %w(index edit new show _form).each do |view| @@ -418,6 +418,6 @@ class NamespacedScaffoldGeneratorTest < NamespacedGeneratorTestCase assert_match(%r(require_dependency "test_app/application_controller"), content) end assert_file "test/controllers/test_app/admin/roles_controller_test.rb", - /module TestApp\n class Admin::RolesControllerTest < ActionController::TestCase/ + /module TestApp\n class Admin::RolesControllerTest < ActionDispatch::IntegrationTest/ end end diff --git a/railties/test/generators/resource_generator_test.rb b/railties/test/generators/resource_generator_test.rb index 581d80d60e..addaf83bc8 100644 --- a/railties/test/generators/resource_generator_test.rb +++ b/railties/test/generators/resource_generator_test.rb @@ -33,7 +33,7 @@ class ResourceGeneratorTest < Rails::Generators::TestCase def test_resource_controller_with_pluralized_class_name run_generator assert_file "app/controllers/accounts_controller.rb", /class AccountsController < ApplicationController/ - assert_file "test/controllers/accounts_controller_test.rb", /class AccountsControllerTest < ActionController::TestCase/ + assert_file "test/controllers/accounts_controller_test.rb", /class AccountsControllerTest < ActionDispatch::IntegrationTest/ assert_file "app/helpers/accounts_helper.rb", /module AccountsHelper/ end diff --git a/railties/test/generators/scaffold_controller_generator_test.rb b/railties/test/generators/scaffold_controller_generator_test.rb index 95ef853a11..c37e289f4b 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 @@ -104,10 +104,10 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase run_generator ["User", "name:string", "age:integer", "organization:references{polymorphic}"] assert_file "test/controllers/users_controller_test.rb" do |content| - assert_match(/class UsersControllerTest < ActionController::TestCase/, content) + assert_match(/class UsersControllerTest < ActionDispatch::IntegrationTest/, content) assert_match(/test "should get index"/, content) - assert_match(/post :create, params: \{ user: \{ age: @user\.age, name: @user\.name, organization_id: @user\.organization_id, organization_type: @user\.organization_type \} \}/, content) - assert_match(/patch :update, params: \{ id: @user, user: \{ age: @user\.age, name: @user\.name, organization_id: @user\.organization_id, organization_type: @user\.organization_type \} \}/, content) + assert_match(/post users_url, params: \{ user: \{ age: @user\.age, name: @user\.name, organization_id: @user\.organization_id, organization_type: @user\.organization_type \} \}/, content) + assert_match(/patch user_url\(@user\), params: \{ user: \{ age: @user\.age, name: @user\.name, organization_id: @user\.organization_id, organization_type: @user\.organization_type \} \}/, content) end end @@ -115,10 +115,10 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase run_generator ["User"] assert_file "test/controllers/users_controller_test.rb" do |content| - assert_match(/class UsersControllerTest < ActionController::TestCase/, content) + assert_match(/class UsersControllerTest < ActionDispatch::IntegrationTest/, content) assert_match(/test "should get index"/, content) - assert_match(/post :create, params: \{ user: \{ \} \}/, content) - assert_match(/patch :update, params: \{ id: @user, user: \{ \} \}/, content) + assert_match(/post users_url, params: \{ user: \{ \} \}/, content) + assert_match(/patch user_url\(@user\), params: \{ user: \{ \} \}/, content) end end @@ -236,10 +236,10 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase run_generator ["User", "name:string", "age:integer", "organization:references{polymorphic}", "--api"] assert_file "test/controllers/users_controller_test.rb" do |content| - assert_match(/class UsersControllerTest < ActionController::TestCase/, content) + assert_match(/class UsersControllerTest < ActionDispatch::IntegrationTest/, content) assert_match(/test "should get index"/, content) - assert_match(/post :create, params: \{ user: \{ age: @user\.age, name: @user\.name, organization_id: @user\.organization_id, organization_type: @user\.organization_type \} \}/, content) - assert_match(/patch :update, params: \{ id: @user, user: \{ age: @user\.age, name: @user\.name, organization_id: @user\.organization_id, organization_type: @user\.organization_type \} \}/, content) + assert_match(/post users_url, params: \{ user: \{ age: @user\.age, name: @user\.name, organization_id: @user\.organization_id, organization_type: @user\.organization_type \} \}/, content) + assert_match(/patch user_url\(@user\), params: \{ user: \{ age: @user\.age, name: @user\.name, organization_id: @user\.organization_id, organization_type: @user\.organization_type \} \}/, content) assert_no_match(/assert_redirected_to/, content) end end diff --git a/railties/test/generators/scaffold_generator_test.rb b/railties/test/generators/scaffold_generator_test.rb index 0c3808a9a0..eb81ea3d0e 100644 --- a/railties/test/generators/scaffold_generator_test.rb +++ b/railties/test/generators/scaffold_generator_test.rb @@ -57,9 +57,9 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase end assert_file "test/controllers/product_lines_controller_test.rb" do |test| - assert_match(/class ProductLinesControllerTest < ActionController::TestCase/, test) - assert_match(/post :create, params: \{ product_line: \{ product_id: @product_line\.product_id, title: @product_line\.title, user_id: @product_line\.user_id \} \}/, test) - assert_match(/patch :update, params: \{ id: @product_line, product_line: \{ product_id: @product_line\.product_id, title: @product_line\.title, user_id: @product_line\.user_id \} \}/, test) + assert_match(/class ProductLinesControllerTest < ActionDispatch::IntegrationTest/, test) + assert_match(/post product_lines_url, params: \{ product_line: \{ product_id: @product_line\.product_id, title: @product_line\.title, user_id: @product_line\.user_id \} \}/, test) + assert_match(/patch product_line_url\(@product_line\), params: \{ product_line: \{ product_id: @product_line\.product_id, title: @product_line\.title, user_id: @product_line\.user_id \} \}/, test) end # Views @@ -135,9 +135,9 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase end assert_file "test/controllers/product_lines_controller_test.rb" do |test| - assert_match(/class ProductLinesControllerTest < ActionController::TestCase/, test) - assert_match(/post :create, params: \{ product_line: \{ product_id: @product_line\.product_id, title: @product_line\.title, user_id: @product_line\.user_id \} \}/, test) - assert_match(/patch :update, params: \{ id: @product_line, product_line: \{ product_id: @product_line\.product_id, title: @product_line\.title, user_id: @product_line\.user_id \} \}/, test) + assert_match(/class ProductLinesControllerTest < ActionDispatch::IntegrationTest/, test) + assert_match(/post product_lines_url, params: \{ product_line: \{ product_id: @product_line\.product_id, title: @product_line\.title, user_id: @product_line\.user_id \} \}/, test) + assert_match(/patch product_line_url\(@product_line\), params: \{ product_line: \{ product_id: @product_line\.product_id, title: @product_line\.title, user_id: @product_line\.user_id \} \}/, test) assert_no_match(/assert_redirected_to/, test) end @@ -161,10 +161,10 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase run_generator ["product_line"] assert_file "test/controllers/product_lines_controller_test.rb" do |content| - assert_match(/class ProductLinesControllerTest < ActionController::TestCase/, content) + assert_match(/class ProductLinesControllerTest < ActionDispatch::IntegrationTest/, content) assert_match(/test "should get index"/, content) - assert_match(/post :create, params: \{ product_line: \{ \} \}/, content) - assert_match(/patch :update, params: \{ id: @product_line, product_line: \{ \} \}/, content) + assert_match(/post product_lines_url, params: \{ product_line: \{ \} \}/, content) + assert_match(/patch product_line_url\(@product_line\), params: \{ product_line: \{ \} \}/, content) end end @@ -250,7 +250,7 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase end assert_file "test/controllers/admin/roles_controller_test.rb", - /class Admin::RolesControllerTest < ActionController::TestCase/ + /class Admin::RolesControllerTest < ActionDispatch::IntegrationTest/ # Views %w(index edit new show _form).each do |view| diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb index 24386de82a..4a47ab32b4 100644 --- a/railties/test/railties/engine_test.rb +++ b/railties/test/railties/engine_test.rb @@ -63,22 +63,22 @@ module RailtiesTest test "copying migrations" do @plugin.write "db/migrate/1_create_users.rb", <<-RUBY - class CreateUsers < ActiveRecord::Migration + class CreateUsers < ActiveRecord::Migration::Current end RUBY @plugin.write "db/migrate/2_add_last_name_to_users.rb", <<-RUBY - class AddLastNameToUsers < ActiveRecord::Migration + class AddLastNameToUsers < ActiveRecord::Migration::Current end RUBY @plugin.write "db/migrate/3_create_sessions.rb", <<-RUBY - class CreateSessions < ActiveRecord::Migration + class CreateSessions < ActiveRecord::Migration::Current end RUBY app_file "db/migrate/1_create_sessions.rb", <<-RUBY - class CreateSessions < ActiveRecord::Migration + class CreateSessions < ActiveRecord::Migration::Current def up end end @@ -123,12 +123,12 @@ module RailtiesTest end @plugin.write "db/migrate/1_create_users.rb", <<-RUBY - class CreateUsers < ActiveRecord::Migration + class CreateUsers < ActiveRecord::Migration::Current end RUBY @blog.write "db/migrate/2_create_blogs.rb", <<-RUBY - class CreateBlogs < ActiveRecord::Migration + class CreateBlogs < ActiveRecord::Migration::Current end RUBY @@ -163,11 +163,11 @@ module RailtiesTest end @core.write "db/migrate/1_create_users.rb", <<-RUBY - class CreateUsers < ActiveRecord::Migration; end + class CreateUsers < ActiveRecord::Migration::Current; end RUBY @api.write "db/migrate/2_create_keys.rb", <<-RUBY - class CreateKeys < ActiveRecord::Migration; end + class CreateKeys < ActiveRecord::Migration::Current; end RUBY boot_rails @@ -190,7 +190,7 @@ module RailtiesTest RUBY @plugin.write "db/migrate/0_add_first_name_to_users.rb", <<-RUBY - class AddFirstNameToUsers < ActiveRecord::Migration + class AddFirstNameToUsers < ActiveRecord::Migration::Current end RUBY |