diff options
267 files changed, 2085 insertions, 3082 deletions
@@ -11,7 +11,7 @@ task :build => "all:build" desc "Release all gems to rubygems and create a tag" task :release => "all:release" -PROJECTS = %w(activesupport activemodel actionpack actionmailer activerecord railties) +PROJECTS = %w(activesupport activemodel actionpack actionview actionmailer activerecord railties) desc 'Run all tests by default' task :default => %w(test test:isolated) @@ -47,20 +47,25 @@ task :install => :build do end desc "Generate documentation for the Rails framework" -Rails::API::RepoTask.new('rdoc') +if ENV['EDGE'] + Rails::API::EdgeTask.new('rdoc') +else + Rails::API::StableTask.new('rdoc') +end desc 'Bump all versions to match version.rb' task :update_versions do require File.dirname(__FILE__) + "/version" File.open("RAILS_VERSION", "w") do |f| - f.puts Rails.version + f.puts Rails::VERSION::STRING end constants = { "activesupport" => "ActiveSupport", "activemodel" => "ActiveModel", "actionpack" => "ActionPack", + "actionview" => "ActionView", "actionmailer" => "ActionMailer", "activerecord" => "ActiveRecord", "railties" => "Rails" diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 05495179c3..ea1d090bc2 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,3 +1,35 @@ +* Fix `ActionDispatch::ParamsParser#parse_formatted_parameters` to rewind body input stream on + parsing json params. + + Fixes #11345 + + *Yuri Bol*, *Paul Nikitochkin* + +* Ignore spaces around delimiter in Set-Cookie header. + + *Yamagishi Kazutoshi* + +* Remove deprecated Rails application fallback for integration testing, set + `ActionDispatch.test_app` instead. + + *Carlos Antonio da Silva* + +* Remove deprecated `page_cache_extension` config. + + *Francesco Rodriguez* + +* Remove deprecated constants from Action Controller: + + ActionController::AbstractRequest => ActionDispatch::Request + ActionController::Request => ActionDispatch::Request + ActionController::AbstractResponse => ActionDispatch::Response + ActionController::Response => ActionDispatch::Response + ActionController::Routing => ActionDispatch::Routing + ActionController::Integration => ActionDispatch::Integration + ActionController::IntegrationTest => ActionDispatch::IntegrationTest + + *Carlos Antonio da Silva* + * Fix `Mime::Type.parse` when bad accepts header is looked up. Previously it was setting `request.formats` with an array containing a `nil` value, which raised an error when setting the controller formats. diff --git a/actionpack/RUNNING_UNIT_TESTS.rdoc b/actionpack/RUNNING_UNIT_TESTS.rdoc index 1b29abd2d1..08767ae133 100644 --- a/actionpack/RUNNING_UNIT_TESTS.rdoc +++ b/actionpack/RUNNING_UNIT_TESTS.rdoc @@ -15,13 +15,3 @@ To run a single test suite which can be further narrowed down to one test: rake test TEST=path/to/test.rb TESTOPTS="--name=test_something" - -== Dependency on Active Record and database setup - -Test cases in the test/active_record/ directory depend on having -activerecord and sqlite installed. If Active Record is not in -actionpack/../activerecord directory, or the sqlite rubygem is not installed, -these tests are skipped. - -Other tests are runnable from a fresh copy of actionpack without any configuration. - diff --git a/actionpack/Rakefile b/actionpack/Rakefile index 114d3a7c0c..97ab30ada0 100644 --- a/actionpack/Rakefile +++ b/actionpack/Rakefile @@ -19,7 +19,7 @@ end namespace :test do task :isolated do ruby = File.join(*RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME')) - Dir.glob("test/{abstract,controller,dispatch}/**/*_test.rb").all? do |file| + Dir.glob("test/{abstract,controller,dispatch,assertions,journey}/**/*_test.rb").all? do |file| sh(ruby, '-w', '-Ilib:test', file) end or raise "Failures" end diff --git a/actionpack/lib/abstract_controller/callbacks.rb b/actionpack/lib/abstract_controller/callbacks.rb index 19cfd7dae1..21c6191691 100644 --- a/actionpack/lib/abstract_controller/callbacks.rb +++ b/actionpack/lib/abstract_controller/callbacks.rb @@ -71,7 +71,7 @@ module AbstractController # * <tt>name</tt> - The callback to be added # * <tt>options</tt> - A hash of options to be used when adding the callback def _insert_callbacks(callbacks, block = nil) - options = callbacks.last.is_a?(Hash) ? callbacks.pop : {} + options = callbacks.extract_options! _normalize_callback_options(options) callbacks.push(block) if block callbacks.each do |callback| diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 9fdad63b45..cc03da4904 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -40,9 +40,6 @@ module ActionController autoload :UrlFor end - autoload :Integration, 'action_controller/deprecated/integration_test' - autoload :IntegrationTest, 'action_controller/deprecated/integration_test' - autoload :Routing, 'action_controller/deprecated' autoload :TestCase, 'action_controller/test_case' autoload :TemplateAssertions, 'action_controller/test_case' diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb index ea33d975ef..5352e5ffe2 100644 --- a/actionpack/lib/action_controller/caching.rb +++ b/actionpack/lib/action_controller/caching.rb @@ -58,16 +58,6 @@ module ActionController config_accessor :default_static_extension self.default_static_extension ||= '.html' - def self.page_cache_extension=(extension) - ActiveSupport::Deprecation.deprecation_warning(:page_cache_extension, :default_static_extension) - self.default_static_extension = extension - end - - def self.page_cache_extension - ActiveSupport::Deprecation.deprecation_warning(:page_cache_extension, :default_static_extension) - default_static_extension - end - config_accessor :perform_caching self.perform_caching = true if perform_caching.nil? diff --git a/actionpack/lib/action_controller/deprecated.rb b/actionpack/lib/action_controller/deprecated.rb deleted file mode 100644 index 2405bebb97..0000000000 --- a/actionpack/lib/action_controller/deprecated.rb +++ /dev/null @@ -1,7 +0,0 @@ -ActionController::AbstractRequest = ActionController::Request = ActionDispatch::Request -ActionController::AbstractResponse = ActionController::Response = ActionDispatch::Response -ActionController::Routing = ActionDispatch::Routing - -ActiveSupport::Deprecation.warn 'ActionController::AbstractRequest and ActionController::Request are deprecated and will be removed, use ActionDispatch::Request instead.' -ActiveSupport::Deprecation.warn 'ActionController::AbstractResponse and ActionController::Response are deprecated and will be removed, use ActionDispatch::Response instead.' -ActiveSupport::Deprecation.warn 'ActionController::Routing is deprecated and will be removed, use ActionDispatch::Routing instead.'
\ No newline at end of file diff --git a/actionpack/lib/action_controller/deprecated/integration_test.rb b/actionpack/lib/action_controller/deprecated/integration_test.rb deleted file mode 100644 index 54eae48f47..0000000000 --- a/actionpack/lib/action_controller/deprecated/integration_test.rb +++ /dev/null @@ -1,5 +0,0 @@ -ActionController::Integration = ActionDispatch::Integration -ActionController::IntegrationTest = ActionDispatch::IntegrationTest - -ActiveSupport::Deprecation.warn 'ActionController::Integration is deprecated and will be removed, use ActionDispatch::Integration instead.' -ActiveSupport::Deprecation.warn 'ActionController::IntegrationTest is deprecated and will be removed, use ActionDispatch::IntegrationTest instead.' diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb index 0cbbbbe1fd..5ed3d2ebc1 100644 --- a/actionpack/lib/action_controller/test_case.rb +++ b/actionpack/lib/action_controller/test_case.rb @@ -524,7 +524,6 @@ module ActionController def process(action, http_method = 'GET', *args) check_required_ivars - http_method, args = handle_old_process_api(http_method, args, caller) if args.first.is_a?(String) && http_method != 'HEAD' @request.env['RAW_POST_DATA'] = args.shift @@ -628,17 +627,6 @@ module ActionController end end - def handle_old_process_api(http_method, args, callstack) - # 4.0: Remove this method. - if http_method.is_a?(Hash) - ActiveSupport::Deprecation.warn("TestCase#process now expects the HTTP method as second argument: process(action, http_method, params, session, flash)", callstack) - args.unshift(http_method) - http_method = args.last.is_a?(String) ? args.last : "GET" - end - - [http_method, args] - end - def build_request_uri(action, parameters) unless @request.env["PATH_INFO"] options = @controller.respond_to?(:url_options) ? @controller.__send__(:url_options).merge(parameters) : parameters diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb index 4ca1d35489..b4cf8ad2f7 100644 --- a/actionpack/lib/action_dispatch/http/request.rb +++ b/actionpack/lib/action_dispatch/http/request.rb @@ -226,7 +226,7 @@ module ActionDispatch def raw_post unless @env.include? 'RAW_POST_DATA' raw_post_body = body - @env['RAW_POST_DATA'] = raw_post_body.read(@env['CONTENT_LENGTH'].to_i) + @env['RAW_POST_DATA'] = raw_post_body.read(content_length) raw_post_body.rewind if raw_post_body.respond_to?(:rewind) end @env['RAW_POST_DATA'] diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb index 5697282791..d3696cbb8a 100644 --- a/actionpack/lib/action_dispatch/http/response.rb +++ b/actionpack/lib/action_dispatch/http/response.rb @@ -210,7 +210,9 @@ module ActionDispatch # :nodoc: if body.respond_to?(:to_path) @stream = body else - @stream = build_buffer self, munge_body_object(body) + synchronize do + @stream = build_buffer self, munge_body_object(body) + end end end diff --git a/actionpack/lib/action_dispatch/journey/visitors.rb b/actionpack/lib/action_dispatch/journey/visitors.rb index 2964d80d9f..0a8cb1b4d4 100644 --- a/actionpack/lib/action_dispatch/journey/visitors.rb +++ b/actionpack/lib/action_dispatch/journey/visitors.rb @@ -4,7 +4,7 @@ module ActionDispatch module Visitors # :nodoc: class Visitor # :nodoc: DISPATCH_CACHE = Hash.new { |h,k| - h[k] = "visit_#{k}" + h[k] = :"visit_#{k}" } def accept(node) diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index d055acb296..3ccd0c9ee8 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -160,7 +160,7 @@ module ActionDispatch end end - # Returns the +signed+ or +encrypted jar, preferring +encrypted+ if +secret_key_base+ is set. + # Returns the +signed+ or +encrypted+ jar, preferring +encrypted+ if +secret_key_base+ is set. # Used by ActionDispatch::Session::CookieStore to avoid the need to introduce new cookie stores. def signed_or_encrypted @signed_or_encrypted ||= diff --git a/actionpack/lib/action_dispatch/middleware/params_parser.rb b/actionpack/lib/action_dispatch/middleware/params_parser.rb index fb70b60ef6..b426183488 100644 --- a/actionpack/lib/action_dispatch/middleware/params_parser.rb +++ b/actionpack/lib/action_dispatch/middleware/params_parser.rb @@ -41,7 +41,7 @@ module ActionDispatch when Proc strategy.call(request.raw_post) when :json - data = ActiveSupport::JSON.decode(request.body) + data = ActiveSupport::JSON.decode(request.raw_post) data = {:_json => data} unless data.is_a?(Hash) Request::Utils.deep_munge(data).with_indifferent_access else diff --git a/actionpack/lib/action_dispatch/middleware/ssl.rb b/actionpack/lib/action_dispatch/middleware/ssl.rb index 8d5ab19f60..999c022535 100644 --- a/actionpack/lib/action_dispatch/middleware/ssl.rb +++ b/actionpack/lib/action_dispatch/middleware/ssl.rb @@ -57,7 +57,7 @@ module ActionDispatch cookies = cookies.split("\n") headers['Set-Cookie'] = cookies.map { |cookie| - if cookie !~ /;\s+secure(;|$)/i + if cookie !~ /;\s*secure\s*(;|$)/i "#{cookie}; secure" else cookie diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb index 1f899a434c..9beb30307b 100644 --- a/actionpack/lib/action_dispatch/testing/integration.rb +++ b/actionpack/lib/action_dispatch/testing/integration.rb @@ -298,8 +298,6 @@ module ActionDispatch session = Rack::Test::Session.new(_mock_session) - env.merge!(env) - # NOTE: rack-test v0.5 doesn't build a default uri correctly # Make sure requested path is always a full uri uri = URI.parse('/') @@ -489,10 +487,6 @@ module ActionDispatch @@app = nil def self.app - if !@@app && !ActionDispatch.test_app - ActiveSupport::Deprecation.warn "Rails application fallback is deprecated and no longer works, please set ActionDispatch.test_app" - end - @@app || ActionDispatch.test_app end diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb index e521c6ce96..a67dff5436 100644 --- a/actionpack/test/controller/caching_test.rb +++ b/actionpack/test/controller/caching_test.rb @@ -312,18 +312,3 @@ class ViewCacheDependencyTest < ActionController::TestCase assert_equal %w(trombone flute), HasDependenciesController.new.view_cache_dependencies end end - -class DeprecatedPageCacheExtensionTest < ActiveSupport::TestCase - def test_page_cache_extension_binds_default_static_extension - deprecation_behavior = ActiveSupport::Deprecation.behavior - ActiveSupport::Deprecation.behavior = :silence - old_extension = ActionController::Base.default_static_extension - - ActionController::Base.page_cache_extension = '.rss' - - assert_equal '.rss', ActionController::Base.default_static_extension - ensure - ActiveSupport::Deprecation.behavior = deprecation_behavior - ActionController::Base.default_static_extension = old_extension - end -end diff --git a/actionpack/test/controller/test_case_test.rb b/actionpack/test/controller/test_case_test.rb index 7c27458f46..f75c604277 100644 --- a/actionpack/test/controller/test_case_test.rb +++ b/actionpack/test/controller/test_case_test.rb @@ -280,13 +280,6 @@ XML assert_equal "/test_case_test/test/test_uri/7", @response.body end - def test_process_with_old_api - assert_deprecated do - process :test_uri, :id => 7 - assert_equal "/test_case_test/test/test_uri/7", @response.body - end - end - def test_process_with_request_uri_with_params_with_explicit_uri @request.env['PATH_INFO'] = "/explicit/uri" process :test_uri, "GET", :id => 7 diff --git a/actionpack/test/dispatch/request/json_params_parsing_test.rb b/actionpack/test/dispatch/request/json_params_parsing_test.rb index b62ed6a8b2..dba9ab688f 100644 --- a/actionpack/test/dispatch/request/json_params_parsing_test.rb +++ b/actionpack/test/dispatch/request/json_params_parsing_test.rb @@ -70,6 +70,13 @@ class JsonParamsParsingTest < ActionDispatch::IntegrationTest end end + test 'raw_post is not empty for JSON request' do + with_test_routing do + post '/parse', '{"posts": [{"title": "Post Title"}]}', 'CONTENT_TYPE' => 'application/json' + assert_equal '{"posts": [{"title": "Post Title"}]}', request.raw_post + end + end + private def assert_parses(expected, actual, headers = {}) with_test_routing do diff --git a/actionpack/test/dispatch/request/multipart_params_parsing_test.rb b/actionpack/test/dispatch/request/multipart_params_parsing_test.rb index 3c30a705e9..2a2f92b5b3 100644 --- a/actionpack/test/dispatch/request/multipart_params_parsing_test.rb +++ b/actionpack/test/dispatch/request/multipart_params_parsing_test.rb @@ -108,8 +108,7 @@ class MultipartParamsParsingTest < ActionDispatch::IntegrationTest # Rack doesn't handle multipart/mixed for us. files = params['files'] - files.force_encoding('ASCII-8BIT') - assert_equal 19756, files.size + assert_equal 19756, files.bytesize end test "does not create tempfile if no file has been selected" do diff --git a/actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb b/actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb index 9169658c22..9a77454f30 100644 --- a/actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb +++ b/actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb @@ -17,10 +17,9 @@ class UrlEncodedParamsParsingTest < ActionDispatch::IntegrationTest end test "parses unbalanced query string with array" do - assert_parses( - {'location' => ["1", "2"], 'age_group' => ["2"]}, - "location[]=1&location[]=2&age_group[]=2" - ) + query = "location[]=1&location[]=2&age_group[]=2" + expected = { 'location' => ["1", "2"], 'age_group' => ["2"] } + assert_parses expected, query end test "parses nested hash" do @@ -30,9 +29,17 @@ class UrlEncodedParamsParsingTest < ActionDispatch::IntegrationTest "note[viewers][viewer][][type]=Group", "note[viewers][viewer][][id]=2" ].join("&") - - expected = { "note" => { "viewers"=>{"viewer"=>[{ "id"=>"1", "type"=>"User"}, {"type"=>"Group", "id"=>"2"} ]} } } - assert_parses(expected, query) + expected = { + "note" => { + "viewers" => { + "viewer" => [ + { "id" => "1", "type" => "User" }, + { "type" => "Group", "id" => "2" } + ] + } + } + } + assert_parses expected, query end test "parses more complex nesting" do @@ -48,7 +55,6 @@ class UrlEncodedParamsParsingTest < ActionDispatch::IntegrationTest "products[second]=Pc", "=Save" ].join("&") - expected = { "customers" => { "boston" => { @@ -70,13 +76,12 @@ class UrlEncodedParamsParsingTest < ActionDispatch::IntegrationTest "second" => "Pc" } } - assert_parses expected, query end test "parses params with array" do - query = "selected[]=1&selected[]=2&selected[]=3" - expected = { "selected" => [ "1", "2", "3" ] } + query = "selected[]=1&selected[]=2&selected[]=3" + expected = { "selected" => ["1", "2", "3"] } assert_parses expected, query end @@ -88,13 +93,13 @@ class UrlEncodedParamsParsingTest < ActionDispatch::IntegrationTest test "parses params with array prefix and hashes" do query = "a[][b][c]=d" - expected = {"a" => [{"b" => {"c" => "d"}}]} + expected = { "a" => [{ "b" => { "c" => "d" } }] } assert_parses expected, query end test "parses params with complex nesting" do query = "a[][b][c][][d][]=e" - expected = {"a" => [{"b" => {"c" => [{"d" => ["e"]}]}}]} + expected = { "a" => [{ "b" => { "c" => [{ "d" => ["e"] }] } }] } assert_parses expected, query end @@ -104,7 +109,6 @@ class UrlEncodedParamsParsingTest < ActionDispatch::IntegrationTest "something_else=blah", "logo=#{File.expand_path(__FILE__)}" ].join("&") - expected = { "customers" => { "boston" => { @@ -116,22 +120,20 @@ class UrlEncodedParamsParsingTest < ActionDispatch::IntegrationTest "something_else" => "blah", "logo" => File.expand_path(__FILE__), } - assert_parses expected, query end test "parses params with Safari 2 trailing null character" do - query = "selected[]=1&selected[]=2&selected[]=3\0" - expected = { "selected" => [ "1", "2", "3" ] } + query = "selected[]=1&selected[]=2&selected[]=3\0" + expected = { "selected" => ["1", "2", "3"] } assert_parses expected, query end test "ambiguous params returns a bad request" do with_routing do |set| set.draw do - post ':action', :to => ::UrlEncodedParamsParsingTest::TestController + post ':action', to: ::UrlEncodedParamsParsingTest::TestController end - post "/parse", "foo[]=bar&foo[4]=bar" assert_response :bad_request end @@ -141,7 +143,7 @@ class UrlEncodedParamsParsingTest < ActionDispatch::IntegrationTest def with_test_routing with_routing do |set| set.draw do - post ':action', :to => ::UrlEncodedParamsParsingTest::TestController + post ':action', to: ::UrlEncodedParamsParsingTest::TestController end yield end @@ -151,8 +153,8 @@ class UrlEncodedParamsParsingTest < ActionDispatch::IntegrationTest with_test_routing do post "/parse", actual assert_response :ok - assert_equal(expected, TestController.last_request_parameters) - assert_utf8(TestController.last_request_parameters) + assert_equal expected, TestController.last_request_parameters + assert_utf8 TestController.last_request_parameters end end @@ -167,11 +169,11 @@ class UrlEncodedParamsParsingTest < ActionDispatch::IntegrationTest object.each_value do |v| case v when Hash - assert_utf8(v) + assert_utf8 v when Array - v.each {|el| assert_utf8(el) } + v.each { |el| assert_utf8 el } else - assert_utf8(v) + assert_utf8 v end end end diff --git a/actionpack/test/dispatch/ssl_test.rb b/actionpack/test/dispatch/ssl_test.rb index 61b55f3d82..94969f795a 100644 --- a/actionpack/test/dispatch/ssl_test.rb +++ b/actionpack/test/dispatch/ssl_test.rb @@ -124,6 +124,35 @@ class SSLTest < ActionDispatch::IntegrationTest response.headers['Set-Cookie'].split("\n") end + + def test_flag_cookies_as_secure_with_has_not_spaces_before + self.app = ActionDispatch::SSL.new(lambda { |env| + headers = { + 'Content-Type' => "text/html", + 'Set-Cookie' => "problem=def; path=/;secure; HttpOnly" + } + [200, headers, ["OK"]] + }) + + get "https://example.org/" + assert_equal ["problem=def; path=/;secure; HttpOnly"], + response.headers['Set-Cookie'].split("\n") + end + + def test_flag_cookies_as_secure_with_has_not_spaces_after + self.app = ActionDispatch::SSL.new(lambda { |env| + headers = { + 'Content-Type' => "text/html", + 'Set-Cookie' => "problem=def; path=/; secure;HttpOnly" + } + [200, headers, ["OK"]] + }) + + get "https://example.org/" + assert_equal ["problem=def; path=/; secure;HttpOnly"], + response.headers['Set-Cookie'].split("\n") + end + def test_flag_cookies_as_secure_with_ignore_case self.app = ActionDispatch::SSL.new(lambda { |env| headers = { diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md index 31e123d916..2981e76ec5 100644 --- a/actionview/CHANGELOG.md +++ b/actionview/CHANGELOG.md @@ -1,3 +1,61 @@ +* Fix `link_to` with block and url hashes. + + Before: + + link_to(action: 'bar', controller: 'foo') { content_tag(:span, 'Example site') } + # => "<a action=\"bar\" controller=\"foo\"><span>Example site</span></a>" + + After: + + link_to(action: 'bar', controller: 'foo') { content_tag(:span, 'Example site') } + # => "<a href=\"/\"><span>Example site</span></a>" + + *Murahashi Sanemat Kenichi* + +* Fix "Stack Level Too Deep" error when redering recursive partials. + + Fixes #11340. + + *Rafael Mendonça França* + +* Added an `enforce_utf8` hash option for `form_tag` method. + + Control to output a hidden input tag with name `utf8` without monkey + patching. + + Before: + + form_tag + # => '<form>..<input name="utf8" type="hidden" value="✓" />..</form>' + + After: + + form_tag + # => '<form>..<input name="utf8" type="hidden" value="✓" />..</form>' + + form_tag({}, { :enforce_utf8 => false }) + # => '<form>....</form>' + + *ma2gedev* + +* Remove the deprecated `include_seconds` argument from `distance_of_time_in_words`, + pass in an `:include_seconds` hash option to use this feature. + + *Carlos Antonio da Silva* + +* Remove deprecated block passing to `FormBuilder#new`. + + *Vipul A M* + +* Pick `DateField` `DateTimeField` and `ColorField` values from stringified options allowing use of symbol keys with helpers. + + *Jon Rowe* + +* Remove the deprecated `prompt` argument from `grouped_options_for_select`, + pass in a `:prompt` hash option to use this feature. + + *kennyj* + * Always escape the result of `link_to_unless` method. Before: diff --git a/actionview/MIT-LICENSE b/actionview/MIT-LICENSE index 810daf856c..5c668d9624 100644 --- a/actionview/MIT-LICENSE +++ b/actionview/MIT-LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2012 David Heinemeier Hansson +Copyright (c) 2004-2013 David Heinemeier Hansson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/actionview/RUNNING_UNIT_TESTS b/actionview/RUNNING_UNIT_TESTS.rdoc index a0c35e1810..a0c35e1810 100644 --- a/actionview/RUNNING_UNIT_TESTS +++ b/actionview/RUNNING_UNIT_TESTS.rdoc diff --git a/actionview/Rakefile b/actionview/Rakefile index 1cbd35cda4..25365d3c3d 100644 --- a/actionview/Rakefile +++ b/actionview/Rakefile @@ -1,5 +1,4 @@ require 'rake/testtask' -require 'rake/packagetask' require 'rubygems/package_task' desc "Default Task" diff --git a/actionview/lib/action_view/digestor.rb b/actionview/lib/action_view/digestor.rb index 9324a1ac50..a674e4d7ea 100644 --- a/actionview/lib/action_view/digestor.rb +++ b/actionview/lib/action_view/digestor.rb @@ -7,10 +7,14 @@ module ActionView @@cache = ThreadSafe::Cache.new def self.digest(name, format, finder, options = {}) - cache_key = [name, format] + Array.wrap(options[:dependencies]) - @@cache[cache_key.join('.')] ||= begin + cache_key = ([name, format] + Array.wrap(options[:dependencies])).join('.') + @@cache.fetch(cache_key) do + @@cache[cache_key] ||= nil if options[:partial] # Prevent re-entry + klass = options[:partial] || name.include?("/_") ? PartialDigestor : Digestor - klass.new(name, format, finder, options).digest + digest = klass.new(name, format, finder, options).digest + + @@cache[cache_key] = digest # Store the value end end diff --git a/actionview/lib/action_view/helpers/date_helper.rb b/actionview/lib/action_view/helpers/date_helper.rb index 8fb5eb1548..8e1aea50a9 100644 --- a/actionview/lib/action_view/helpers/date_helper.rb +++ b/actionview/lib/action_view/helpers/date_helper.rb @@ -64,15 +64,7 @@ module ActionView # distance_of_time_in_words(from_time, to_time, include_seconds: true) # => about 6 years # distance_of_time_in_words(to_time, from_time, include_seconds: true) # => about 6 years # distance_of_time_in_words(Time.now, Time.now) # => less than a minute - def distance_of_time_in_words(from_time, to_time = 0, include_seconds_or_options = {}, options = {}) - if include_seconds_or_options.is_a?(Hash) - options = include_seconds_or_options - else - ActiveSupport::Deprecation.warn "distance_of_time_in_words and time_ago_in_words now accept :include_seconds " + - "as a part of options hash, not a boolean argument" - options[:include_seconds] ||= !!include_seconds_or_options - end - + def distance_of_time_in_words(from_time, to_time = 0, options = {}) options = { scope: :'datetime.distance_in_words' }.merge!(options) diff --git a/actionview/lib/action_view/helpers/form_helper.rb b/actionview/lib/action_view/helpers/form_helper.rb index f64c0ca30b..8a4830d887 100644 --- a/actionview/lib/action_view/helpers/form_helper.rb +++ b/actionview/lib/action_view/helpers/form_helper.rb @@ -1237,11 +1237,7 @@ module ActionView self end - def initialize(object_name, object, template, options, block=nil) - if block - ActiveSupport::Deprecation.warn "Giving a block to FormBuilder is deprecated and has no effect anymore." - end - + def initialize(object_name, object, template, options) @nested_child_index = {} @object_name, @object, @template, @options = object_name, object, template, options @default_options = @options ? @options.slice(:index, :namespace) : {} diff --git a/actionview/lib/action_view/helpers/form_options_helper.rb b/actionview/lib/action_view/helpers/form_options_helper.rb index 21f892f443..4e9ef94ff3 100644 --- a/actionview/lib/action_view/helpers/form_options_helper.rb +++ b/actionview/lib/action_view/helpers/form_options_helper.rb @@ -510,15 +510,8 @@ module ActionView # <b>Note:</b> Only the <tt><optgroup></tt> and <tt><option></tt> tags are returned, so you still have to # wrap the output in an appropriate <tt><select></tt> tag. def grouped_options_for_select(grouped_options, selected_key = nil, options = {}) - if options.is_a?(Hash) - prompt = options[:prompt] - divider = options[:divider] - else - prompt = options - message = "Passing the prompt to grouped_options_for_select as an argument is deprecated. " \ - "Please use an options hash like `{ prompt: #{prompt.inspect} }`." - ActiveSupport::Deprecation.warn message - end + prompt = options[:prompt] + divider = options[:divider] body = "".html_safe @@ -783,7 +776,7 @@ module ActionView # Wraps ActionView::Helpers::FormOptionsHelper#grouped_collection_select for form builders: # # <%= form_for @city do |f| %> - # <%= f.grouped_collection_select :country_id, :country_id, @continents, :countries, :name, :id, :name %> + # <%= f.grouped_collection_select :country_id, @continents, :countries, :name, :id, :name %> # <%= f.submit %> # <% end %> # diff --git a/actionview/lib/action_view/helpers/form_tag_helper.rb b/actionview/lib/action_view/helpers/form_tag_helper.rb index 3fa7696b83..142c27ace0 100644 --- a/actionview/lib/action_view/helpers/form_tag_helper.rb +++ b/actionview/lib/action_view/helpers/form_tag_helper.rb @@ -38,6 +38,7 @@ module ActionView # * A list of parameters to feed to the URL the form will be posted to. # * <tt>:remote</tt> - If set to true, will allow the Unobtrusive JavaScript drivers to control the # submit behavior. By default this behavior is an ajax submit. + # * <tt>:enforce_utf8</tt> - If set to false, a hidden input with name utf8 is not output. # # ==== Examples # form_tag('/posts') @@ -719,7 +720,8 @@ module ActionView method_tag(method) + token_tag(authenticity_token) end - tags = utf8_enforcer_tag << method_tag + enforce_utf8 = html_options.delete("enforce_utf8") { true } + tags = (enforce_utf8 ? utf8_enforcer_tag : ''.html_safe) << method_tag content_tag(:div, tags, :style => 'margin:0;padding:0;display:inline') end diff --git a/actionview/lib/action_view/helpers/tags/color_field.rb b/actionview/lib/action_view/helpers/tags/color_field.rb index d8fc797035..b4bbe92746 100644 --- a/actionview/lib/action_view/helpers/tags/color_field.rb +++ b/actionview/lib/action_view/helpers/tags/color_field.rb @@ -4,7 +4,7 @@ module ActionView class ColorField < TextField # :nodoc: def render options = @options.stringify_keys - options["value"] = @options.fetch("value") { validate_color_string(value(object)) } + options["value"] ||= validate_color_string(value(object)) @options = options super end diff --git a/actionview/lib/action_view/helpers/tags/datetime_field.rb b/actionview/lib/action_view/helpers/tags/datetime_field.rb index 9a2279c611..25e7e05ec6 100644 --- a/actionview/lib/action_view/helpers/tags/datetime_field.rb +++ b/actionview/lib/action_view/helpers/tags/datetime_field.rb @@ -4,7 +4,7 @@ module ActionView class DatetimeField < TextField # :nodoc: def render options = @options.stringify_keys - options["value"] = @options.fetch("value") { format_date(value(object)) } + options["value"] ||= format_date(value(object)) options["min"] = format_date(options["min"]) options["max"] = format_date(options["max"]) @options = options diff --git a/actionview/lib/action_view/helpers/url_helper.rb b/actionview/lib/action_view/helpers/url_helper.rb index 19e5941971..daa9a393b3 100644 --- a/actionview/lib/action_view/helpers/url_helper.rb +++ b/actionview/lib/action_view/helpers/url_helper.rb @@ -172,7 +172,7 @@ module ActionView # link_to "Visit Other Site", "http://www.rubyonrails.org/", data: { confirm: "Are you sure?" } # # => <a href="http://www.rubyonrails.org/" data-confirm="Are you sure?">Visit Other Site</a> def link_to(name = nil, options = nil, html_options = nil, &block) - html_options, options = options, name if block_given? + html_options, options, name = options, name, block if block_given? options ||= {} html_options = convert_options_to_data_attributes(options, html_options) diff --git a/actionview/test/fixtures/digestor/level/_recursion.html.erb b/actionview/test/fixtures/digestor/level/_recursion.html.erb new file mode 100644 index 0000000000..ee5aaf09c3 --- /dev/null +++ b/actionview/test/fixtures/digestor/level/_recursion.html.erb @@ -0,0 +1 @@ +<%= render 'recursion' %> diff --git a/actionview/test/fixtures/digestor/level/recursion.html.erb b/actionview/test/fixtures/digestor/level/recursion.html.erb new file mode 100644 index 0000000000..ee5aaf09c3 --- /dev/null +++ b/actionview/test/fixtures/digestor/level/recursion.html.erb @@ -0,0 +1 @@ +<%= render 'recursion' %> diff --git a/actionview/test/template/digestor_test.rb b/actionview/test/template/digestor_test.rb index 06735c30d3..07f8d36d93 100644 --- a/actionview/test/template/digestor_test.rb +++ b/actionview/test/template/digestor_test.rb @@ -95,6 +95,31 @@ class TemplateDigestorTest < ActionView::TestCase end end + def test_recursion_in_renders + assert digest("level/recursion") # assert recursion is possible + assert_not_nil digest("level/recursion") # assert digest is stored + end + + def test_chaning_the_top_templete_on_recursion + assert digest("level/recursion") # assert recursion is possible + + assert_digest_difference("level/recursion") do + change_template("level/recursion") + end + + assert_not_nil digest("level/recursion") # assert digest is stored + end + + def test_chaning_the_partial_templete_on_recursion + assert digest("level/recursion") # assert recursion is possible + + assert_digest_difference("level/recursion") do + change_template("level/_recursion") + end + + assert_not_nil digest("level/recursion") # assert digest is stored + end + def test_dont_generate_a_digest_for_missing_templates assert_equal '', digest("nothing/there") end diff --git a/actionview/test/template/form_helper_test.rb b/actionview/test/template/form_helper_test.rb index 1ff320224d..f05b6d0d94 100644 --- a/actionview/test/template/form_helper_test.rb +++ b/actionview/test/template/form_helper_test.rb @@ -702,6 +702,11 @@ class FormHelperTest < ActionView::TestCase assert_dom_equal(expected, color_field("car", "color")) end + def test_color_field_with_value_attr + expected = %{<input id="car_color" name="car[color]" type="color" value="#00FF00" />} + assert_dom_equal(expected, color_field("car", "color", value: "#00FF00")) + end + def test_search_field expected = %{<input id="contact_notes_query" name="contact[notes_query]" type="search" />} assert_dom_equal(expected, search_field("contact", "notes_query")) @@ -732,6 +737,12 @@ class FormHelperTest < ActionView::TestCase assert_dom_equal(expected, date_field("post", "written_on", min: min_value, max: max_value, step: step)) end + def test_date_field_with_value_attr + expected = %{<input id="post_written_on" name="post[written_on]" type="date" value="2013-06-29" />} + value = Date.new(2013,6,29) + assert_dom_equal(expected, date_field("post", "written_on", value: value)) + end + def test_date_field_with_timewithzone_value previous_time_zone, Time.zone = Time.zone, 'UTC' expected = %{<input id="post_written_on" name="post[written_on]" type="date" value="2004-06-15" />} @@ -802,6 +813,12 @@ class FormHelperTest < ActionView::TestCase assert_dom_equal(expected, datetime_field("post", "written_on", min: min_value, max: max_value, step: step)) end + def test_datetime_field_with_value_attr + expected = %{<input id="post_written_on" name="post[written_on]" type="datetime" value="2013-06-29T13:37:00+00:00" />} + value = DateTime.new(2013,6,29,13,37) + assert_dom_equal(expected, datetime_field("post", "written_on", value: value)) + end + def test_datetime_field_with_timewithzone_value previous_time_zone, Time.zone = Time.zone, 'UTC' expected = %{<input id="post_written_on" name="post[written_on]" type="datetime" value="2004-06-15T15:30:45.000+0000" />} @@ -2899,18 +2916,6 @@ class FormHelperTest < ActionView::TestCase assert_equal "fields", output end - def test_form_builder_block_argument_deprecation - builder_class = Class.new(ActionView::Helpers::FormBuilder) do - def initialize(object_name, object, template, options, block) - super - end - end - - assert_deprecated(/Giving a block to FormBuilder is deprecated and has no effect anymore/) do - builder_class.new(:foo, nil, nil, {}, proc {}) - end - end - def test_form_for_only_instantiates_builder_once initialization_count = 0 builder_class = Class.new(ActionView::Helpers::FormBuilder) do diff --git a/actionview/test/template/form_options_helper_test.rb b/actionview/test/template/form_options_helper_test.rb index 1715902927..8c90a58a84 100644 --- a/actionview/test/template/form_options_helper_test.rb +++ b/actionview/test/template/form_options_helper_test.rb @@ -310,15 +310,6 @@ class FormOptionsHelperTest < ActionView::TestCase ) end - def test_grouped_options_for_select_with_selected_and_prompt_deprecated - assert_deprecated 'Passing the prompt to grouped_options_for_select as an argument is deprecated. Please use an options hash like `{ prompt: "Choose a product..." }`.' do - assert_dom_equal( - "<option value=\"\">Choose a product...</option><optgroup label=\"Hats\"><option value=\"Baseball Cap\">Baseball Cap</option>\n<option selected=\"selected\" value=\"Cowboy Hat\">Cowboy Hat</option></optgroup>", - grouped_options_for_select([["Hats", ["Baseball Cap","Cowboy Hat"]]], "Cowboy Hat", "Choose a product...") - ) - end - end - def test_grouped_options_for_select_with_selected_and_prompt assert_dom_equal( "<option value=\"\">Choose a product...</option><optgroup label=\"Hats\"><option value=\"Baseball Cap\">Baseball Cap</option>\n<option selected=\"selected\" value=\"Cowboy Hat\">Cowboy Hat</option></optgroup>", @@ -337,14 +328,6 @@ class FormOptionsHelperTest < ActionView::TestCase assert grouped_options_for_select([["Hats", ["Baseball Cap","Cowboy Hat"]]]).html_safe? end - def test_grouped_options_for_select_with_prompt_returns_html_escaped_string_deprecated - ActiveSupport::Deprecation.silence do - assert_dom_equal( - "<option value=\"\"><Choose One></option><optgroup label=\"Hats\"><option value=\"Baseball Cap\">Baseball Cap</option>\n<option value=\"Cowboy Hat\">Cowboy Hat</option></optgroup>", - grouped_options_for_select([["Hats", ["Baseball Cap","Cowboy Hat"]]], nil, '<Choose One>')) - end - end - def test_grouped_options_for_select_with_prompt_returns_html_escaped_string assert_dom_equal( "<option value=\"\"><Choose One></option><optgroup label=\"Hats\"><option value=\"Baseball Cap\">Baseball Cap</option>\n<option value=\"Cowboy Hat\">Cowboy Hat</option></optgroup>", diff --git a/actionview/test/template/form_tag_helper_test.rb b/actionview/test/template/form_tag_helper_test.rb index 70fc6a588b..22bf438a56 100644 --- a/actionview/test/template/form_tag_helper_test.rb +++ b/actionview/test/template/form_tag_helper_test.rb @@ -12,9 +12,10 @@ class FormTagHelperTest < ActionView::TestCase def hidden_fields(options = {}) method = options[:method] + enforce_utf8 = options.fetch(:enforce_utf8, true) txt = %{<div style="margin:0;padding:0;display:inline">} - txt << %{<input name="utf8" type="hidden" value="✓" />} + txt << %{<input name="utf8" type="hidden" value="✓" />} if enforce_utf8 if method && !%w(get post).include?(method.to_s) txt << %{<input name="_method" type="hidden" value="#{method}" />} end @@ -110,6 +111,20 @@ class FormTagHelperTest < ActionView::TestCase assert_dom_equal expected, actual end + def test_form_tag_enforce_utf8_true + actual = form_tag({}, { :enforce_utf8 => true }) + expected = whole_form("http://www.example.com", :enforce_utf8 => true) + assert_dom_equal expected, actual + assert actual.html_safe? + end + + def test_form_tag_enforce_utf8_false + actual = form_tag({}, { :enforce_utf8 => false }) + expected = whole_form("http://www.example.com", :enforce_utf8 => false) + assert_dom_equal expected, actual + assert actual.html_safe? + end + def test_form_tag_with_block_in_erb output_buffer = render_erb("<%= form_tag('http://www.example.com') do %>Hello world!<% end %>") diff --git a/actionview/test/template/url_helper_test.rb b/actionview/test/template/url_helper_test.rb index 8373d7f992..851ea8796f 100644 --- a/actionview/test/template/url_helper_test.rb +++ b/actionview/test/template/url_helper_test.rb @@ -308,6 +308,13 @@ class UrlHelperTest < ActiveSupport::TestCase link_to('/', class: "special") { content_tag(:span, 'Example site') } end + def test_link_tag_using_block_and_hash + assert_dom_equal( + %{<a href="/"><span>Example site</span></a>}, + link_to(url_hash) { content_tag(:span, 'Example site') } + ) + end + def test_link_tag_using_block_in_erb out = render_erb %{<%= link_to('/') do %>Example site<% end %>} assert_equal '<a href="/">Example site</a>', out @@ -336,8 +343,6 @@ class UrlHelperTest < ActiveSupport::TestCase assert_dom_equal %{<a href="/">Listing</a>}, link_to_unless(false, "Listing", url_hash) - assert_equal "Showing", link_to_unless(true, "Showing", url_hash) - assert_equal "<strong>Showing</strong>", link_to_unless(true, "Showing", url_hash) { |name| "<strong>#{name}</strong>".html_safe @@ -357,7 +362,6 @@ class UrlHelperTest < ActiveSupport::TestCase def test_link_to_if assert_equal "Showing", link_to_if(false, "Showing", url_hash) assert_dom_equal %{<a href="/">Listing</a>}, link_to_if(true, "Listing", url_hash) - assert_equal "Showing", link_to_if(false, "Showing", url_hash) end def request_for_url(url, opts = {}) diff --git a/activemodel/examples/validations.rb b/activemodel/examples/validations.rb index 2c5cc11f49..c94cd17e18 100644 --- a/activemodel/examples/validations.rb +++ b/activemodel/examples/validations.rb @@ -25,5 +25,5 @@ person1 = Person.new p person1.valid? # => false p person1.errors.messages # => {:name=>["can't be blank"]} -person2 = Person.new(:name => "matz") +person2 = Person.new(name: 'matz') p person2.valid? # => true diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb index 3bd5531356..ef4f2514be 100644 --- a/activemodel/lib/active_model.rb +++ b/activemodel/lib/active_model.rb @@ -37,7 +37,6 @@ module ActiveModel autoload :ForbiddenAttributesProtection autoload :Lint autoload :Model - autoload :DeprecatedMassAssignmentSecurity autoload :Name, 'active_model/naming' autoload :Naming autoload :SecurePassword diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb index 98cde8ba59..f336c759d2 100644 --- a/activemodel/lib/active_model/attribute_methods.rb +++ b/activemodel/lib/active_model/attribute_methods.rb @@ -1,4 +1,5 @@ require 'thread_safe' +require 'mutex_m' module ActiveModel # Raised when an attribute is not defined. @@ -218,6 +219,16 @@ module ActiveModel end end + # Is +new_name+ an alias? + def attribute_alias?(new_name) + attribute_aliases.key? new_name.to_s + end + + # Returns the original name for the alias +name+ + def attribute_alias(name) + attribute_aliases[name.to_s] + end + # Declares the attributes that should be prefixed and suffixed by # ActiveModel::AttributeMethods. # @@ -322,9 +333,10 @@ module ActiveModel attribute_method_matchers_cache.clear end - # Returns true if the attribute methods defined have been generated. def generated_attribute_methods #:nodoc: - @generated_attribute_methods ||= Module.new.tap { |mod| include mod } + @generated_attribute_methods ||= Module.new { + extend Mutex_m + }.tap { |mod| include mod } end protected @@ -388,14 +400,6 @@ module ActiveModel AttributeMethodMatch = Struct.new(:target, :attr_name, :method_name) def initialize(options = {}) - if options[:prefix] == '' || options[:suffix] == '' - message = "Specifying an empty prefix/suffix for an attribute method is no longer " \ - "necessary. If the un-prefixed/suffixed version of the method has not been " \ - "defined when `define_attribute_methods` is called, it will be defined " \ - "automatically." - ActiveSupport::Deprecation.warn message - end - @prefix, @suffix = options.fetch(:prefix, ''), options.fetch(:suffix, '') @regex = /^(?:#{Regexp.escape(@prefix)})(.*)(?:#{Regexp.escape(@suffix)})$/ @method_missing_target = "#{@prefix}attribute#{@suffix}" diff --git a/activemodel/lib/active_model/deprecated_mass_assignment_security.rb b/activemodel/lib/active_model/deprecated_mass_assignment_security.rb deleted file mode 100644 index 1f409c87b9..0000000000 --- a/activemodel/lib/active_model/deprecated_mass_assignment_security.rb +++ /dev/null @@ -1,21 +0,0 @@ -module ActiveModel - module DeprecatedMassAssignmentSecurity # :nodoc: - extend ActiveSupport::Concern - - module ClassMethods # :nodoc: - def attr_protected(*args) - raise "`attr_protected` is extracted out of Rails into a gem. " \ - "Please use new recommended protection model for params" \ - "(strong_parameters) or add `protected_attributes` to your " \ - "Gemfile to use old one." - end - - def attr_accessible(*args) - raise "`attr_accessible` is extracted out of Rails into a gem. " \ - "Please use new recommended protection model for params" \ - "(strong_parameters) or add `protected_attributes` to your " \ - "Gemfile to use old one." - end - end - end -end diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb index 9341f689de..e608a649f8 100644 --- a/activemodel/lib/active_model/errors.rb +++ b/activemodel/lib/active_model/errors.rb @@ -50,7 +50,7 @@ module ActiveModel # # The above allows you to do: # - # p = Person.new + # person = Person.new # person.validate! # => ["can not be nil"] # person.errors.full_messages # => ["name can not be nil"] # # etc.. diff --git a/activemodel/lib/active_model/validations/callbacks.rb b/activemodel/lib/active_model/validations/callbacks.rb index cabb9482f2..fde53b9f89 100644 --- a/activemodel/lib/active_model/validations/callbacks.rb +++ b/activemodel/lib/active_model/validations/callbacks.rb @@ -58,7 +58,9 @@ module ActiveModel if options.is_a?(Hash) && options[:on] options[:if] = Array(options[:if]) options[:on] = Array(options[:on]) - options[:if].unshift("#{options[:on]}.include? self.validation_context") + options[:if].unshift lambda { |o| + options[:on].include? o.validation_context + } end set_callback(:validation, :before, *args, &block) end diff --git a/activemodel/lib/active_model/validations/clusivity.rb b/activemodel/lib/active_model/validations/clusivity.rb index c5aacb010a..1c35cb7c35 100644 --- a/activemodel/lib/active_model/validations/clusivity.rb +++ b/activemodel/lib/active_model/validations/clusivity.rb @@ -15,15 +15,15 @@ module ActiveModel private def include?(record, value) - exclusions = if delimiter.respond_to?(:call) - delimiter.call(record) - elsif delimiter.respond_to?(:to_sym) - record.send(delimiter) - else - delimiter - end + members = if delimiter.respond_to?(:call) + delimiter.call(record) + elsif delimiter.respond_to?(:to_sym) + record.send(delimiter) + else + delimiter + end - exclusions.send(inclusion_method(exclusions), value) + members.send(inclusion_method(members), value) end def delimiter diff --git a/activemodel/test/cases/attribute_methods_test.rb b/activemodel/test/cases/attribute_methods_test.rb index 25eb4860e3..e9cb5ccc96 100644 --- a/activemodel/test/cases/attribute_methods_test.rb +++ b/activemodel/test/cases/attribute_methods_test.rb @@ -202,17 +202,6 @@ class AttributeMethodsTest < ActiveModel::TestCase assert_equal 'bar', m.foo_test end - test 'explicitly specifying an empty prefix/suffix is deprecated' do - klass = Class.new(ModelWithAttributes) - - assert_deprecated { klass.attribute_method_suffix '' } - assert_deprecated { klass.attribute_method_prefix '' } - - klass.define_attribute_methods(:foo) - - assert_equal 'value of foo', klass.new.foo - end - test 'should not interfere with method_missing if the attr has a private/protected method' do m = ModelWithAttributes2.new m.attributes = { 'private_method' => '<3', 'protected_method' => 'O_o' } diff --git a/activemodel/test/cases/deprecated_mass_assignment_security_test.rb b/activemodel/test/cases/deprecated_mass_assignment_security_test.rb deleted file mode 100644 index c1fe8822cd..0000000000 --- a/activemodel/test/cases/deprecated_mass_assignment_security_test.rb +++ /dev/null @@ -1,16 +0,0 @@ -require 'cases/helper' -require 'models/project' - -class DeprecatedMassAssignmentSecurityTest < ActiveModel::TestCase - def test_attr_accessible_raise_error - assert_raise RuntimeError, /protected_attributes/ do - Project.attr_accessible :username - end - end - - def test_attr_protected_raise_error - assert_raise RuntimeError, /protected_attributes/ do - Project.attr_protected :username - end - end -end diff --git a/activemodel/test/cases/serializers/xml_serialization_test.rb b/activemodel/test/cases/serializers/xml_serialization_test.rb index 901f42f29b..c4cfb0c255 100644 --- a/activemodel/test/cases/serializers/xml_serialization_test.rb +++ b/activemodel/test/cases/serializers/xml_serialization_test.rb @@ -130,7 +130,7 @@ class XmlSerializationTest < ActiveModel::TestCase end test "should serialize nil" do - assert_match %r{<pseudonyms nil=\"true\"/>}, @contact.to_xml(methods: :pseudonyms) + assert_match %r{<pseudonyms nil="true"/>}, @contact.to_xml(methods: :pseudonyms) end test "should serialize integer" do @@ -138,23 +138,23 @@ class XmlSerializationTest < ActiveModel::TestCase end test "should serialize datetime" do - assert_match %r{<created-at type=\"dateTime\">2006-08-01T00:00:00Z</created-at>}, @contact.to_xml + assert_match %r{<created-at type="dateTime">2006-08-01T00:00:00Z</created-at>}, @contact.to_xml end test "should serialize boolean" do - assert_match %r{<awesome type=\"boolean\">false</awesome>}, @contact.to_xml + assert_match %r{<awesome type="boolean">false</awesome>}, @contact.to_xml end test "should serialize array" do - assert_match %r{<social type=\"array\">\s*<social>twitter</social>\s*<social>github</social>\s*</social>}, @contact.to_xml(methods: :social) + assert_match %r{<social type="array">\s*<social>twitter</social>\s*<social>github</social>\s*</social>}, @contact.to_xml(methods: :social) end test "should serialize hash" do - assert_match %r{<network>\s*<git type=\"symbol\">github</git>\s*</network>}, @contact.to_xml(methods: :network) + assert_match %r{<network>\s*<git type="symbol">github</git>\s*</network>}, @contact.to_xml(methods: :network) end test "should serialize yaml" do - assert_match %r{<preferences type=\"yaml\">--- !ruby/struct:Customer(\s*)\nname: John\n</preferences>}, @contact.to_xml + assert_match %r{<preferences type="yaml">--- !ruby/struct:Customer(\s*)\nname: John\n</preferences>}, @contact.to_xml end test "should call proc on object" do diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index e4cd15c71e..1eb2f2d130 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,179 @@ +* Remove extra select and update queries on save/touch/destroy ActiveRecord model + with belongs to reflection with option `touch: true`. + + Fixes: #11288 + + *Paul Nikitochkin* + +* Remove deprecated nil-passing to the following `SchemaCache` methods: + `primary_keys`, `tables`, `columns` and `columns_hash`. + + *Yves Senn* + +* Remove deprecated block filter from `ActiveRecord::Migrator#migrate`. + + *Yves Senn* + +* Remove deprecated String constructor from `ActiveRecord::Migrator`. + + *Yves Senn* + +* Remove deprecated `scope` use without passing a callable object. + + *Arun Agrawal* + +* Remove deprecated `transaction_joinable=` in favor of `begin_transaction` + with `:joinable` option. + + *Arun Agrawal* + +* Remove deprecated `decrement_open_transactions`. + + *Arun Agrawal* + +* Remove deprecated `increment_open_transactions`. + + *Arun Agrawal* + +* Remove deprecated `PostgreSQLAdapter#outside_transaction?` + method. You can use `#transaction_open?` instead. + + *Yves Senn* + +* Remove deprecated `ActiveRecord::Fixtures.find_table_name` in favor of + `ActiveRecord::Fixtures.default_fixture_model_name`. + + *Vipul A M* + +* Removed deprecated `columns_for_remove` from `SchemaStatements`. + + *Neeraj Singh* + +* Remove deprecated `SchemaStatements#distinct`. + + *Francesco Rodriguez* + +* Move deprecated `ActiveRecord::TestCase` into the rails test + suite. The class is no longer public and is only used for internal + Rails tests. + + *Yves Senn* + +* Removed support for deprecated option `:restrict` for `:dependent` + in associations. + + *Neeraj Singh* + +* Removed support for deprecated `delete_sql` in associations. + + *Neeraj Singh* + +* Removed support for deprecated `insert_sql` in associations. + + *Neeraj Singh* + +* Removed support for deprecated `finder_sql` in associations. + + *Neeraj Singh* + +* Support array as root element in JSON fields. + + *Alexey Noskov & Francesco Rodriguez* + +* Removed support for deprecated `counter_sql` in associations. + + *Neeraj Singh* + +* Do not invoke callbacks when `delete_all` is called on collection. + + Method `delete_all` should not be invoking callbacks and this + feature was deprecated in Rails 4.0. This is being removed. + `delete_all` will continue to honor the `:dependent` option. However + if `:dependent` value is `:destroy` then the default deletion + strategy for that collection will be applied. + + User can also force a deletion strategy by passing parameter to + `delete_all`. For example you can do `@post.comments.delete_all(:nullify)` . + + *Neeraj Singh* + +* Calling default_scope without a proc will now raise `ArgumentError`. + + *Neeraj Singh* + +* Removed deprecated method `type_cast_code` from Column. + + *Neeraj Singh* + +* Removed deprecated options `delete_sql` and `insert_sql` from HABTM + association. + + Removed deprecated options `finder_sql` and `counter_sql` from + collection association. + + *Neeraj Singh* + +* Remove deprecated `ActiveRecord::Base#connection` method. + Make sure to access it via the class. + + *Yves Senn* + +* Remove deprecation warning for `auto_explain_threshold_in_seconds`. + + *Yves Senn* + +* Remove deprecated `:distinct` option from `Relation#count`. + + *Yves Senn* + +* Removed deprecated methods `partial_updates`, `partial_updates?` and + `partial_updates=`. + + *Neeraj Singh* + +* Removed deprecated method `scoped` + + *Neeraj Singh* + +* Removed deprecated method `default_scopes?` + + *Neeraj Singh* + +* Remove implicit join references that were deprecated in 4.0. + + Example: + + # before with implicit joins + Comment.where('posts.author_id' => 7) + + # after + Comment.references(:posts).where('posts.author_id' => 7) + + *Yves Senn* + +* Apply default scope when joining associations. For example: + + class Post < ActiveRecord::Base + default_scope -> { where published: true } + end + + class Comment + belongs_to :post + end + + When calling `Comment.joins(:post)`, we expect to receive only + comments on published posts, since that is the default scope for + posts. + + Before this change, the default scope from `Post` was not applied, + so we'd get comments on unpublished posts. + + *Jon Leighton* + +* Remove `activerecord-deprecated_finders` as a dependency + + *Łukasz Strzałkowski* + * Remove Oracle / Sqlserver / Firebird database tasks that were deprecated in 4.0. *kennyj* diff --git a/activerecord/activerecord.gemspec b/activerecord/activerecord.gemspec index 337106cb92..9986ded904 100644 --- a/activerecord/activerecord.gemspec +++ b/activerecord/activerecord.gemspec @@ -25,5 +25,4 @@ Gem::Specification.new do |s| s.add_dependency 'activemodel', version s.add_dependency 'arel', '~> 4.0.0' - s.add_dependency 'activerecord-deprecated_finders', '~> 1.0.2' end diff --git a/activerecord/examples/performance.rb b/activerecord/examples/performance.rb index 91697b3c9c..d3546ce948 100644 --- a/activerecord/examples/performance.rb +++ b/activerecord/examples/performance.rb @@ -43,6 +43,8 @@ class Exhibit < ActiveRecord::Base def self.feel(exhibits) exhibits.each { |e| e.feel } end end +def progress_bar(int); print "." if (int%100).zero? ; end + puts 'Generating data...' module ActiveRecord @@ -75,7 +77,7 @@ notes = ActiveRecord::Faker::LOREM.join ' ' today = Date.today puts "Inserting #{RECORDS} users and exhibits..." -RECORDS.times do +RECORDS.times do |record| user = User.create( created_at: today, name: ActiveRecord::Faker.name, @@ -88,7 +90,9 @@ RECORDS.times do user: user, notes: notes ) + progress_bar(record) end +puts "Done!\n" Benchmark.ips(TIME) do |x| ar_obj = Exhibit.find(1) @@ -117,10 +121,18 @@ Benchmark.ips(TIME) do |x| Exhibit.first.look end + x.report 'Model.take' do + Exhibit.take + end + x.report("Model.all limit(100)") do Exhibit.look Exhibit.limit(100) end + x.report("Model.all take(100)") do + Exhibit.look Exhibit.take(100) + end + x.report "Model.all limit(100) with relationship" do Exhibit.feel Exhibit.limit(100).includes(:user) end @@ -167,6 +179,6 @@ Benchmark.ips(TIME) do |x| end x.report "AR.execute(query)" do - ActiveRecord::Base.connection.execute("Select * from exhibits where id = #{(rand * 1000 + 1).to_i}") + ActiveRecord::Base.connection.execute("SELECT * FROM exhibits WHERE id = #{(rand * 1000 + 1).to_i}") end end diff --git a/activerecord/examples/simple.rb b/activerecord/examples/simple.rb index ee3c42a05b..4ed5d80eb2 100644 --- a/activerecord/examples/simple.rb +++ b/activerecord/examples/simple.rb @@ -1,4 +1,4 @@ -$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib" +require File.expand_path('../../../load_paths', __FILE__) require 'active_record' class Person < ActiveRecord::Base diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index 994cacb4f9..9d418dacaf 100644 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -25,7 +25,6 @@ require 'active_support' require 'active_support/rails' require 'active_model' require 'arel' -require 'active_record/deprecated_finders' require 'active_record/version' @@ -152,7 +151,6 @@ module ActiveRecord autoload :OracleDatabaseTasks, 'active_record/tasks/oracle_database_tasks' end - autoload :TestCase autoload :TestFixtures, 'active_record/fixtures' def self.eager_load! diff --git a/activerecord/lib/active_record/associations/association.rb b/activerecord/lib/active_record/associations/association.rb index ee62298793..338d5d2afe 100644 --- a/activerecord/lib/active_record/associations/association.rb +++ b/activerecord/lib/active_record/associations/association.rb @@ -84,11 +84,6 @@ module ActiveRecord target_scope.merge(association_scope) end - def scoped - ActiveSupport::Deprecation.warn "#scoped is deprecated. use #scope instead." - scope - end - # The scope for this association. # # Note that the association_scope is merged into the target_scope only when the @@ -122,11 +117,7 @@ module ActiveRecord # Can be overridden (i.e. in ThroughAssociation) to merge in other scopes (i.e. the # through association's scope) def target_scope - all = klass.all - scope = AssociationRelation.new(klass, klass.arel_table, self) - scope.merge! all - scope.default_scoped = all.default_scoped? - scope + AssociationRelation.new(klass, klass.arel_table, self).merge!(klass.all) end # Loads the \target if needed and returns it. diff --git a/activerecord/lib/active_record/associations/builder/association.rb b/activerecord/lib/active_record/associations/builder/association.rb index 3254da4677..bbc1c20f60 100644 --- a/activerecord/lib/active_record/associations/builder/association.rb +++ b/activerecord/lib/active_record/associations/builder/association.rb @@ -104,13 +104,6 @@ module ActiveRecord::Associations::Builder raise ArgumentError, "The :dependent option must be one of #{valid_dependent_options}, but is :#{options[:dependent]}" end - if options[:dependent] == :restrict - ActiveSupport::Deprecation.warn( - "The :restrict option is deprecated. Please use :restrict_with_exception instead, which " \ - "provides the same functionality." - ) - end - n = name model.before_destroy lambda { |o| o.association(n).handle_dependency } end diff --git a/activerecord/lib/active_record/associations/builder/belongs_to.rb b/activerecord/lib/active_record/associations/builder/belongs_to.rb index d4e1a0dda1..81293e464d 100644 --- a/activerecord/lib/active_record/associations/builder/belongs_to.rb +++ b/activerecord/lib/active_record/associations/builder/belongs_to.rb @@ -92,7 +92,7 @@ module ActiveRecord::Associations::Builder end def self.touch_record(o, foreign_key, name, touch) # :nodoc: - old_foreign_id = o.attribute_was(foreign_key) + old_foreign_id = o.changed_attributes[foreign_key] if old_foreign_id klass = o.association(name).klass diff --git a/activerecord/lib/active_record/associations/builder/collection_association.rb b/activerecord/lib/active_record/associations/builder/collection_association.rb index 9c6690b721..5eb11f98c8 100644 --- a/activerecord/lib/active_record/associations/builder/collection_association.rb +++ b/activerecord/lib/active_record/associations/builder/collection_association.rb @@ -8,7 +8,7 @@ module ActiveRecord::Associations::Builder CALLBACKS = [:before_add, :after_add, :before_remove, :after_remove] def valid_options - super + [:table_name, :finder_sql, :counter_sql, :before_add, + super + [:table_name, :before_add, :after_add, :before_remove, :after_remove, :extend] end @@ -20,7 +20,6 @@ module ActiveRecord::Associations::Builder end def build - show_deprecation_warnings wrap_block_extension reflection = super CALLBACKS.each { |callback_name| define_callback(callback_name) } @@ -31,14 +30,6 @@ module ActiveRecord::Associations::Builder true end - def show_deprecation_warnings - [:finder_sql, :counter_sql].each do |name| - if options.include? name - ActiveSupport::Deprecation.warn("The :#{name} association option is deprecated. Please find an alternative (such as using scopes).") - end - end - end - def wrap_block_extension if block_extension @extension_module = mod = Module.new(&block_extension) diff --git a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb index bdac02b5bf..64ee24c7c6 100644 --- a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb +++ b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb @@ -5,7 +5,7 @@ module ActiveRecord::Associations::Builder end def valid_options - super + [:join_table, :association_foreign_key, :delete_sql, :insert_sql] + super + [:join_table, :association_foreign_key] end def build @@ -14,16 +14,6 @@ module ActiveRecord::Associations::Builder reflection end - def show_deprecation_warnings - super - - [:delete_sql, :insert_sql].each do |name| - if options.include? name - ActiveSupport::Deprecation.warn("The :#{name} association option is deprecated. Please find an alternative (such as using has_many :through).") - end - end - end - def define_destroy_hook name = self.name model.send(:include, Module.new { diff --git a/activerecord/lib/active_record/associations/builder/has_many.rb b/activerecord/lib/active_record/associations/builder/has_many.rb index 0d1bdd21ee..a60cb4769a 100644 --- a/activerecord/lib/active_record/associations/builder/has_many.rb +++ b/activerecord/lib/active_record/associations/builder/has_many.rb @@ -9,7 +9,7 @@ module ActiveRecord::Associations::Builder end def valid_dependent_options - [:destroy, :delete_all, :nullify, :restrict, :restrict_with_error, :restrict_with_exception] + [:destroy, :delete_all, :nullify, :restrict_with_error, :restrict_with_exception] end end end diff --git a/activerecord/lib/active_record/associations/builder/has_one.rb b/activerecord/lib/active_record/associations/builder/has_one.rb index 0da564f402..2406437a07 100644 --- a/activerecord/lib/active_record/associations/builder/has_one.rb +++ b/activerecord/lib/active_record/associations/builder/has_one.rb @@ -19,7 +19,7 @@ module ActiveRecord::Associations::Builder end def valid_dependent_options - [:destroy, :delete, :nullify, :restrict, :restrict_with_error, :restrict_with_exception] + [:destroy, :delete, :nullify, :restrict_with_error, :restrict_with_exception] end end end diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb index 9833822f8f..6ddbd4955d 100644 --- a/activerecord/lib/active_record/associations/collection_association.rb +++ b/activerecord/lib/active_record/associations/collection_association.rb @@ -44,7 +44,7 @@ module ActiveRecord # Implements the ids reader method, e.g. foo.item_ids for Foo.has_many :items def ids_reader - if loaded? || options[:finder_sql] + if loaded? load_target.map do |record| record.send(reflection.association_primary_key) end @@ -79,9 +79,7 @@ module ActiveRecord if block_given? load_target.find(*args) { |*block_args| yield(*block_args) } else - if options[:finder_sql] - find_by_scan(*args) - elsif options[:inverse_of] && loaded? + if options[:inverse_of] && loaded? args = args.flatten raise RecordNotFound, "Couldn't find #{scope.klass.name} without an ID" if args.blank? @@ -153,11 +151,35 @@ module ActiveRecord end end - # Remove all records from this association. + # Removes all records from the association without calling callbacks + # on the associated records. It honors the `:dependent` option. However + # if the `:dependent` value is `:destroy` then in that case the default + # deletion strategy for the association is applied. + # + # You can force a particular deletion strategy by passing a parameter. + # + # Example: + # + # @author.books.delete_all(:nullify) + # @author.books.delete_all(:delete_all) # # See delete for more info. - def delete_all - delete(:all).tap do + def delete_all(dependent = nil) + if dependent.present? && ![:nullify, :delete_all].include?(dependent) + raise ArgumentError, "Valid values are :nullify or :delete_all" + end + + dependent = if dependent.present? + dependent + elsif options[:dependent] == :destroy + # since delete_all should not invoke callbacks so use the default deletion strategy + # for :destroy + reflection.is_a?(ActiveRecord::Reflection::ThroughReflection) ? :delete_all : :nullify + else + options[:dependent] + end + + delete(:all, dependent: dependent).tap do reset loaded! end @@ -173,36 +195,27 @@ module ActiveRecord end end - # Count all records using SQL. If the +:counter_sql+ or +:finder_sql+ option is set for the - # association, it will be used for the query. Otherwise, construct options and pass them with + # Count all records using SQL. Construct options and pass them with # scope to the target class's +count+. def count(column_name = nil, count_options = {}) column_name, count_options = nil, column_name if column_name.is_a?(Hash) - if options[:counter_sql] || options[:finder_sql] - unless count_options.blank? - raise ArgumentError, "If finder_sql/counter_sql is used then options cannot be passed" - end - - reflection.klass.count_by_sql(custom_counter_sql) - else - relation = scope - if association_scope.distinct_value - # This is needed because 'SELECT count(DISTINCT *)..' is not valid SQL. - column_name ||= reflection.klass.primary_key - relation = relation.distinct - end + relation = scope + if association_scope.distinct_value + # This is needed because 'SELECT count(DISTINCT *)..' is not valid SQL. + column_name ||= reflection.klass.primary_key + relation = relation.distinct + end - value = relation.count(column_name) + value = relation.count(column_name) - limit = options[:limit] - offset = options[:offset] + limit = options[:limit] + offset = options[:offset] - if limit || offset - [ [value - offset.to_i, 0].max, limit.to_i ].min - else - value - end + if limit || offset + [ [value - offset.to_i, 0].max, limit.to_i ].min + else + value end end @@ -214,18 +227,10 @@ module ActiveRecord # are actually removed from the database, that depends precisely on # +delete_records+. They are in any case removed from the collection. def delete(*records) - dependent = options[:dependent] + _options = records.extract_options! + dependent = _options[:dependent] || options[:dependent] if records.first == :all - - if dependent && dependent == :destroy - message = 'In Rails 4.1 delete_all on associations would not fire callbacks. ' \ - 'It means if the :dependent option is :destroy then the associated ' \ - 'records would be deleted without loading and invoking callbacks.' - - ActiveRecord::Base.logger ? ActiveRecord::Base.logger.warn(message) : $stderr.puts(message) - end - if loaded? || dependent == :destroy delete_or_destroy(load_target, dependent) else @@ -285,14 +290,14 @@ module ActiveRecord # Returns true if the collection is empty. # - # If the collection has been loaded or the <tt>:counter_sql</tt> option - # is provided, it is equivalent to <tt>collection.size.zero?</tt>. If the + # If the collection has been loaded + # it is equivalent to <tt>collection.size.zero?</tt>. If the # collection has not been loaded, it is equivalent to # <tt>collection.exists?</tt>. If the collection has not already been # loaded and you are going to fetch the records anyway it is better to # check <tt>collection.length.zero?</tt>. def empty? - if loaded? || options[:counter_sql] + if loaded? size.zero? else @target.blank? && !scope.exists? @@ -345,7 +350,6 @@ module ActiveRecord if record.new_record? include_in_memory?(record) else - load_target if options[:finder_sql] loaded? ? target.include?(record) : scope.exists?(record) end else @@ -390,31 +394,8 @@ module ActiveRecord private - def custom_counter_sql - if options[:counter_sql] - interpolate(options[:counter_sql]) - else - # replace the SELECT clause with COUNT(SELECTS), preserving any hints within /* ... */ - interpolate(options[:finder_sql]).sub(/SELECT\b(\/\*.*?\*\/ )?(.*)\bFROM\b/im) do - count_with = $2.to_s - count_with = '*' if count_with.blank? || count_with =~ /,/ || count_with =~ /\.\*/ - "SELECT #{$1}COUNT(#{count_with}) FROM" - end - end - end - - def custom_finder_sql - interpolate(options[:finder_sql]) - end - def find_target - records = - if options[:finder_sql] - reflection.klass.find_by_sql(custom_finder_sql) - else - scope.to_a - end - + records = scope.to_a records.each { |record| set_inverse_instance(record) } records end @@ -553,7 +534,6 @@ module ActiveRecord # Otherwise, go to the database only if none of the following are true: # * target already loaded # * owner is new record - # * custom :finder_sql exists # * target contains new or changed record(s) # * the first arg is an integer (which indicates the number of records to be returned) def fetch_first_or_last_using_find?(args) @@ -562,7 +542,6 @@ module ActiveRecord else !(loaded? || owner.new_record? || - options[:finder_sql] || target.any? { |record| record.new_record? || record.changed? } || args.first.kind_of?(Integer)) end @@ -579,7 +558,7 @@ module ActiveRecord end end - # If using a custom finder_sql or if the :inverse_of option has been + # If the :inverse_of option has been # specified, then #find scans the entire collection. def find_by_scan(*args) expects_array = args.first.kind_of?(Array) diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index 7cdb5ba5b3..6dc2da56d1 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -33,7 +33,6 @@ module ActiveRecord def initialize(klass, association) #:nodoc: @association = association super klass, klass.arel_table - self.default_scoped = true merge! association.scope(nullify: false) end @@ -418,8 +417,8 @@ module ActiveRecord # # Pet.find(1, 2, 3) # # => ActiveRecord::RecordNotFound - def delete_all - @association.delete_all + def delete_all(dependent = nil) + @association.delete_all(dependent) end # Deletes the records of the collection directly from the database @@ -727,7 +726,7 @@ module ActiveRecord end # Returns +true+ if the collection is empty. If the collection has been - # loaded or the <tt>:counter_sql</tt> option is provided, it is equivalent + # loaded it is equivalent # to <tt>collection.size.zero?</tt>. If the collection has not been loaded, # it is equivalent to <tt>collection.exists?</tt>. If the collection has # not already been loaded and you are going to fetch the records anyway it diff --git a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb index bb3e3db379..b2e6c708bf 100644 --- a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb +++ b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb @@ -18,16 +18,12 @@ module ActiveRecord end end - if options[:insert_sql] - owner.connection.insert(interpolate(options[:insert_sql], record)) - else - stmt = join_table.compile_insert( - join_table[reflection.foreign_key] => owner.id, - join_table[reflection.association_foreign_key] => record.id - ) + stmt = join_table.compile_insert( + join_table[reflection.foreign_key] => owner.id, + join_table[reflection.association_foreign_key] => record.id + ) - owner.class.connection.insert stmt - end + owner.class.connection.insert stmt record end @@ -39,22 +35,17 @@ module ActiveRecord end def delete_records(records, method) - if sql = options[:delete_sql] - records = load_target if records == :all - records.each { |record| owner.class.connection.delete(interpolate(sql, record)) } - else - relation = join_table - condition = relation[reflection.foreign_key].eq(owner.id) - - unless records == :all - condition = condition.and( - relation[reflection.association_foreign_key] - .in(records.map { |x| x.id }.compact) - ) - end - - owner.class.connection.delete(relation.where(condition).compile_delete) + relation = join_table + condition = relation[reflection.foreign_key].eq(owner.id) + + unless records == :all + condition = condition.and( + relation[reflection.association_foreign_key] + .in(records.map { |x| x.id }.compact) + ) end + + owner.class.connection.delete(relation.where(condition).compile_delete) end def invertible_for?(record) diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb index cf8a589496..607ed0da46 100644 --- a/activerecord/lib/active_record/associations/has_many_association.rb +++ b/activerecord/lib/active_record/associations/has_many_association.rb @@ -9,7 +9,7 @@ module ActiveRecord def handle_dependency case options[:dependent] - when :restrict, :restrict_with_exception + when :restrict_with_exception raise ActiveRecord::DeleteRestrictionError.new(reflection.name) unless empty? when :restrict_with_error @@ -58,8 +58,6 @@ module ActiveRecord def count_records count = if has_cached_counter? owner.send(:read_attribute, cached_counter_attribute_name) - elsif options[:counter_sql] || options[:finder_sql] - reflection.klass.count_by_sql(custom_counter_sql) else scope.count end diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb index 920038a543..3ab1ea1ff4 100644 --- a/activerecord/lib/active_record/associations/has_one_association.rb +++ b/activerecord/lib/active_record/associations/has_one_association.rb @@ -6,7 +6,7 @@ module ActiveRecord def handle_dependency case options[:dependent] - when :restrict, :restrict_with_exception + when :restrict_with_exception raise ActiveRecord::DeleteRestrictionError.new(reflection.name) if load_target when :restrict_with_error diff --git a/activerecord/lib/active_record/associations/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/join_dependency/join_association.rb index b81aecb4e5..e0715d9dd1 100644 --- a/activerecord/lib/active_record/associations/join_dependency/join_association.rb +++ b/activerecord/lib/active_record/associations/join_dependency/join_association.rb @@ -106,6 +106,8 @@ module ActiveRecord ] end + scope_chain_items += [reflection.klass.send(:build_default_scope)].compact + constraint = scope_chain_items.inject(constraint) do |chain, item| unless item.is_a?(Relation) item = ActiveRecord::Relation.new(reflection.klass, table).instance_exec(self, &item) diff --git a/activerecord/lib/active_record/associations/preloader/association.rb b/activerecord/lib/active_record/associations/preloader/association.rb index 82588905c6..d31f7432cd 100644 --- a/activerecord/lib/active_record/associations/preloader/association.rb +++ b/activerecord/lib/active_record/associations/preloader/association.rb @@ -98,7 +98,6 @@ module ActiveRecord def build_scope scope = klass.unscoped - scope.default_scoped = true values = reflection_scope.values preload_values = preload_scope.values @@ -113,7 +112,7 @@ module ActiveRecord scope.where!(klass.table_name => { reflection.type => model.base_class.sti_name }) end - scope + klass.default_scoped.merge(scope) end end end diff --git a/activerecord/lib/active_record/associations/preloader/through_association.rb b/activerecord/lib/active_record/associations/preloader/through_association.rb index c4b50ab306..de06931845 100644 --- a/activerecord/lib/active_record/associations/preloader/through_association.rb +++ b/activerecord/lib/active_record/associations/preloader/through_association.rb @@ -41,21 +41,21 @@ module ActiveRecord end def through_scope - through_scope = through_reflection.klass.unscoped + scope = through_reflection.klass.unscoped if options[:source_type] - through_scope.where! reflection.foreign_type => options[:source_type] + scope.where! reflection.foreign_type => options[:source_type] else unless reflection_scope.where_values.empty? - through_scope.includes_values = Array(reflection_scope.values[:includes] || options[:source]) - through_scope.where_values = reflection_scope.values[:where] + scope.includes_values = Array(reflection_scope.values[:includes] || options[:source]) + scope.where_values = reflection_scope.values[:where] end - through_scope.references! reflection_scope.values[:references] - through_scope.order! reflection_scope.values[:order] if through_scope.eager_loading? + scope.references! reflection_scope.values[:references] + scope.order! reflection_scope.values[:order] if scope.eager_loading? end - through_scope + scope end end end diff --git a/activerecord/lib/active_record/associations/through_association.rb b/activerecord/lib/active_record/associations/through_association.rb index 35f29b37a2..bd99997e7f 100644 --- a/activerecord/lib/active_record/associations/through_association.rb +++ b/activerecord/lib/active_record/associations/through_association.rb @@ -15,7 +15,7 @@ module ActiveRecord scope = super chain[1..-1].each do |reflection| scope.merge!( - reflection.klass.all.with_default_scope. + reflection.klass.all. except(:select, :create_with, :includes, :preload, :joins, :eager_load) ) end diff --git a/activerecord/lib/active_record/attribute_assignment.rb b/activerecord/lib/active_record/attribute_assignment.rb index 75377bba57..4f06955406 100644 --- a/activerecord/lib/active_record/attribute_assignment.rb +++ b/activerecord/lib/active_record/attribute_assignment.rb @@ -3,7 +3,6 @@ require 'active_model/forbidden_attributes_protection' module ActiveRecord module AttributeAssignment extend ActiveSupport::Concern - include ActiveModel::DeprecatedMassAssignmentSecurity include ActiveModel::ForbiddenAttributesProtection # Allows you to set all the attributes by passing in a hash of attributes with diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index 609c6e8cab..c07fd67216 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -1,4 +1,5 @@ require 'active_support/core_ext/enumerable' +require 'mutex_m' module ActiveRecord # = Active Record Attribute Methods @@ -7,6 +8,7 @@ module ActiveRecord include ActiveModel::AttributeMethods included do + initialize_generated_modules include Read include Write include BeforeTypeCast @@ -17,27 +19,71 @@ module ActiveRecord include Serialization end + AttrNames = Module.new { + def self.set_name_cache(name, value) + const_name = "ATTR_#{name}" + unless const_defined? const_name + const_set const_name, value.dup.freeze + end + end + } + + class AttributeMethodCache + include Mutex_m + + def initialize + super + @module = Module.new + @method_cache = {} + end + + def [](name) + synchronize do + @method_cache.fetch(name) { + safe_name = name.unpack('h*').first + temp_method = "__temp__#{safe_name}" + ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name + @module.module_eval method_body(temp_method, safe_name), __FILE__, __LINE__ + @method_cache[name] = @module.instance_method temp_method + } + end + end + + private + def method_body; raise NotImplementedError; end + end + module ClassMethods + def inherited(child_class) #:nodoc: + child_class.initialize_generated_modules + super + end + + def initialize_generated_modules # :nodoc: + @generated_attribute_methods = Module.new { extend Mutex_m } + @attribute_methods_generated = false + include @generated_attribute_methods + end + # Generates all the attribute related methods for columns in the database # accessors, mutators and query methods. def define_attribute_methods # :nodoc: # Use a mutex; we don't want two thread simultaneously trying to define # attribute methods. - @attribute_methods_mutex.synchronize do - return if attribute_methods_generated? + generated_attribute_methods.synchronize do + return false if @attribute_methods_generated superclass.define_attribute_methods unless self == base_class super(column_names) @attribute_methods_generated = true end - end - - def attribute_methods_generated? # :nodoc: - @attribute_methods_generated ||= false + true end def undefine_attribute_methods # :nodoc: - super if attribute_methods_generated? - @attribute_methods_generated = false + generated_attribute_methods.synchronize do + super if @attribute_methods_generated + @attribute_methods_generated = false + end end # Raises a <tt>ActiveRecord::DangerousAttributeError</tt> exception when an @@ -119,9 +165,7 @@ module ActiveRecord # If we haven't generated any methods yet, generate them, then # see if we've created the method we're looking for. def method_missing(method, *args, &block) # :nodoc: - unless self.class.attribute_methods_generated? - self.class.define_attribute_methods - + if self.class.define_attribute_methods if respond_to_without_attributes?(method) send(method, *args, &block) else @@ -164,7 +208,7 @@ module ActiveRecord # person.respond_to(:nothing) # => false def respond_to?(name, include_private = false) name = name.to_s - self.class.define_attribute_methods unless self.class.attribute_methods_generated? + self.class.define_attribute_methods result = super # If the result is false the answer is false. @@ -174,7 +218,7 @@ module ActiveRecord # For queries selecting a subset of columns, return false for unselected columns. # We check defined?(@attributes) not to issue warnings if called on objects that # have been allocated but not yet initialized. - if defined?(@attributes) && @attributes.present? && self.class.column_names.include?(name) + if defined?(@attributes) && @attributes.any? && self.class.column_names.include?(name) return has_attribute?(name) end diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb index 6315dd9549..dc2399643c 100644 --- a/activerecord/lib/active_record/attribute_methods/dirty.rb +++ b/activerecord/lib/active_record/attribute_methods/dirty.rb @@ -14,17 +14,6 @@ module ActiveRecord class_attribute :partial_writes, instance_writer: false self.partial_writes = true - - def self.partial_updates=(v); self.partial_writes = v; end - def self.partial_updates?; partial_writes?; end - def self.partial_updates; partial_writes; end - - ActiveSupport::Deprecation.deprecate_methods( - singleton_class, - :partial_updates= => :partial_writes=, - :partial_updates? => :partial_writes?, - :partial_updates => :partial_writes - ) end # Attempts to +save+ the record and clears changed attributes if successful. diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb index 506f5d75f9..1cf3aba41c 100644 --- a/activerecord/lib/active_record/attribute_methods/read.rb +++ b/activerecord/lib/active_record/attribute_methods/read.rb @@ -1,6 +1,38 @@ +require 'active_support/core_ext/module/method_transplanting' + module ActiveRecord module AttributeMethods module Read + ReaderMethodCache = Class.new(AttributeMethodCache) { + private + # We want to generate the methods via module_eval rather than + # define_method, because define_method is slower on dispatch. + # Evaluating many similar methods may use more memory as the instruction + # sequences are duplicated and cached (in MRI). define_method may + # be slower on dispatch, but if you're careful about the closure + # created, then define_method will consume much less memory. + # + # But sometimes the database might return columns with + # characters that are not allowed in normal method names (like + # 'my_column(omg)'. So to work around this we first define with + # the __temp__ identifier, and then use alias method to rename + # it to what we want. + # + # We are also defining a constant to hold the frozen string of + # the attribute name. Using a constant means that we do not have + # to allocate an object on each call to the attribute method. + # Making it frozen means that it doesn't get duped when used to + # key the @attributes_cache in read_attribute. + def method_body(method_name, const_name) + <<-EOMETHOD + def #{method_name} + name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{const_name} + read_attribute(name) { |n| missing_attribute(n, caller) } + end + EOMETHOD + end + }.new + extend ActiveSupport::Concern ATTRIBUTE_TYPES_CACHED_BY_DEFAULT = [:datetime, :timestamp, :time, :date] @@ -32,30 +64,30 @@ module ActiveRecord protected - # We want to generate the methods via module_eval rather than - # define_method, because define_method is slower on dispatch and - # uses more memory (because it creates a closure). - # - # But sometimes the database might return columns with - # characters that are not allowed in normal method names (like - # 'my_column(omg)'. So to work around this we first define with - # the __temp__ identifier, and then use alias method to rename - # it to what we want. - # - # We are also defining a constant to hold the frozen string of - # the attribute name. Using a constant means that we do not have - # to allocate an object on each call to the attribute method. - # Making it frozen means that it doesn't get duped when used to - # key the @attributes_cache in read_attribute. - def define_method_attribute(name) - safe_name = name.unpack('h*').first - generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1 - def __temp__#{safe_name} - read_attribute(AttrNames::ATTR_#{safe_name}) { |n| missing_attribute(n, caller) } + if Module.methods_transplantable? + def define_method_attribute(name) + method = ReaderMethodCache[name] + generated_attribute_methods.module_eval { define_method name, method } + end + else + def define_method_attribute(name) + safe_name = name.unpack('h*').first + temp_method = "__temp__#{safe_name}" + + ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name + + generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1 + def #{temp_method} + name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{safe_name} + read_attribute(name) { |n| missing_attribute(n, caller) } + end + STR + + generated_attribute_methods.module_eval do + alias_method name, temp_method + undef_method temp_method end - alias_method #{name.inspect}, :__temp__#{safe_name} - undef_method :__temp__#{safe_name} - STR + end end private diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb index cd33494cc3..c853fc0917 100644 --- a/activerecord/lib/active_record/attribute_methods/write.rb +++ b/activerecord/lib/active_record/attribute_methods/write.rb @@ -1,6 +1,21 @@ +require 'active_support/core_ext/module/method_transplanting' + module ActiveRecord module AttributeMethods module Write + WriterMethodCache = Class.new(AttributeMethodCache) { + private + + def method_body(method_name, const_name) + <<-EOMETHOD + def #{method_name}(value) + name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{const_name} + write_attribute(name, value) + end + EOMETHOD + end + }.new + extend ActiveSupport::Concern included do @@ -10,17 +25,29 @@ module ActiveRecord module ClassMethods protected - # See define_method_attribute in read.rb for an explanation of - # this code. - def define_method_attribute=(name) - safe_name = name.unpack('h*').first - generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1 - def __temp__#{safe_name}=(value) - write_attribute(AttrNames::ATTR_#{safe_name}, value) - end - alias_method #{(name + '=').inspect}, :__temp__#{safe_name}= - undef_method :__temp__#{safe_name}= - STR + if Module.methods_transplantable? + # See define_method_attribute in read.rb for an explanation of + # this code. + def define_method_attribute=(name) + method = WriterMethodCache[name] + generated_attribute_methods.module_eval { + define_method "#{name}=", method + } + end + else + def define_method_attribute=(name) + safe_name = name.unpack('h*').first + ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name + + generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1 + def __temp__#{safe_name}=(value) + name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{safe_name} + write_attribute(name, value) + end + alias_method #{(name + '=').inspect}, :__temp__#{safe_name}= + undef_method :__temp__#{safe_name}= + STR + end end end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb index 816b397fcf..811749c7fd 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -332,11 +332,6 @@ module ActiveRecord end end - def clear_stale_cached_connections! # :nodoc: - reap - end - deprecate :clear_stale_cached_connections! => "Please use #reap instead" - # Check-out a database connection from the pool, indicating that you want # to use it. You should call #checkin when you no longer need this. # diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index 9a1923dec5..4b425494d0 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -694,15 +694,6 @@ module ActiveRecord end end - # SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause. - # - # distinct("posts.id", ["posts.created_at desc"]) - # - def distinct(columns, order_by) - ActiveSupport::Deprecation.warn("#distinct is deprecated and shall be removed from future releases.") - "DISTINCT #{columns_for_distinct(columns, order_by)}" - end - # Given a set of columns and an ORDER BY clause, returns the columns for a SELECT DISTINCT. # Both PostgreSQL and Oracle overrides this for custom DISTINCT syntax - they # require the order columns appear in the SELECT. @@ -812,12 +803,6 @@ module ActiveRecord index_name end - def columns_for_remove(table_name, *column_names) - ActiveSupport::Deprecation.warn("columns_for_remove is deprecated and will be removed in the future") - raise ArgumentError.new("You must specify at least one column name. Example: remove_columns(:people, :first_name)") if column_names.blank? - column_names.map {|column_name| quote_column_name(column_name) } - end - def rename_table_indexes(table_name, new_name) indexes(new_name).each do |index| generated_index_name = index_name(table_name, column: index.columns) diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index e232cad982..cfff7202a3 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -387,20 +387,6 @@ module ActiveRecord @transaction.number end - def increment_open_transactions - ActiveSupport::Deprecation.warn "#increment_open_transactions is deprecated and has no effect" - end - - def decrement_open_transactions - ActiveSupport::Deprecation.warn "#decrement_open_transactions is deprecated and has no effect" - end - - def transaction_joinable=(joinable) - message = "#transaction_joinable= is deprecated. Please pass the :joinable option to #begin_transaction instead." - ActiveSupport::Deprecation.warn message - @transaction.joinable = joinable - end - def create_savepoint end diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb index 609ccc2ed2..cc02b313e1 100644 --- a/activerecord/lib/active_record/connection_adapters/column.rb +++ b/activerecord/lib/active_record/connection_adapters/column.rb @@ -107,30 +107,6 @@ module ActiveRecord end end - def type_cast_code(var_name) - message = "Column#type_cast_code is deprecated in favor of using Column#type_cast only, " \ - "and it is going to be removed in future Rails versions." - ActiveSupport::Deprecation.warn message - - klass = self.class.name - - case type - when :string, :text then var_name - when :integer then "#{klass}.value_to_integer(#{var_name})" - when :float then "#{var_name}.to_f" - when :decimal then "#{klass}.value_to_decimal(#{var_name})" - when :datetime, :timestamp then "#{klass}.string_to_time(#{var_name})" - when :time then "#{klass}.string_to_dummy_time(#{var_name})" - when :date then "#{klass}.value_to_date(#{var_name})" - when :binary then "#{klass}.binary_to_string(#{var_name})" - when :boolean then "#{klass}.value_to_boolean(#{var_name})" - when :hstore then "#{klass}.string_to_hstore(#{var_name})" - when :inet, :cidr then "#{klass}.string_to_cidr(#{var_name})" - when :json then "#{klass}.string_to_json(#{var_name})" - else var_name - end - end - # Returns the human name of the column name. # # ===== Examples diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb index 9b5170f657..751655e61c 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb @@ -218,13 +218,6 @@ module ActiveRecord execute "ROLLBACK" end - def outside_transaction? - message = "#outside_transaction? is deprecated. This method was only really used " \ - "internally, but you can use #transaction_open? instead." - ActiveSupport::Deprecation.warn message - @connection.transaction_status == PGconn::PQTRANS_IDLE - end - def create_savepoint execute("SAVEPOINT #{current_savepoint_name}") end diff --git a/activerecord/lib/active_record/connection_adapters/schema_cache.rb b/activerecord/lib/active_record/connection_adapters/schema_cache.rb index 1d7a22e831..e5c9f6f54a 100644 --- a/activerecord/lib/active_record/connection_adapters/schema_cache.rb +++ b/activerecord/lib/active_record/connection_adapters/schema_cache.rb @@ -1,4 +1,3 @@ -require 'active_support/deprecation/reporting' module ActiveRecord module ConnectionAdapters @@ -16,13 +15,8 @@ module ActiveRecord prepare_default_proc end - def primary_keys(table_name = nil) - if table_name - @primary_keys[table_name] - else - ActiveSupport::Deprecation.warn('call primary_keys with a table name!') - @primary_keys.dup - end + def primary_keys(table_name) + @primary_keys[table_name] end # A cached lookup for table existence. @@ -41,34 +35,19 @@ module ActiveRecord end end - def tables(name = nil) - if name - @tables[name] - else - ActiveSupport::Deprecation.warn('call tables with a name!') - @tables.dup - end + def tables(name) + @tables[name] end # Get the columns for a table - def columns(table = nil) - if table - @columns[table] - else - ActiveSupport::Deprecation.warn('call columns with a table name!') - @columns.dup - end + def columns(table) + @columns[table] end # Get the columns for a table as a hash, key is the column name # value is the column object. - def columns_hash(table = nil) - if table - @columns_hash[table] - else - ActiveSupport::Deprecation.warn('call columns_hash with a table name!') - @columns_hash.dup - end + def columns_hash(table) + @columns_hash[table] end # Clears out internal caches diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb index f0141aaaab..c6b7da2e3c 100644 --- a/activerecord/lib/active_record/core.rb +++ b/activerecord/lib/active_record/core.rb @@ -69,13 +69,10 @@ module ActiveRecord mattr_accessor :timestamped_migrations, instance_writer: false self.timestamped_migrations = true - ## - # :singleton-method: - # Disable implicit join references. This feature was deprecated with Rails 4. - # If you don't make use of implicit references but still see deprecation warnings - # you can disable the feature entirely. This will be the default with Rails 4.1. - mattr_accessor :disable_implicit_join_references, instance_writer: false - self.disable_implicit_join_references = false + def self.disable_implicit_join_references=(value) + ActiveSupport::Deprecation.warn("Implicit join references were removed with Rails 4.1." \ + "Make sure to remove this configuration because it does nothing.") + end class_attribute :default_connection_handler, instance_writer: false @@ -91,20 +88,8 @@ module ActiveRecord end module ClassMethods - def inherited(child_class) #:nodoc: - child_class.initialize_generated_modules - super - end - def initialize_generated_modules - @attribute_methods_mutex = Mutex.new - - # force attribute methods to be higher in inheritance hierarchy than other generated methods - generated_attribute_methods.const_set(:AttrNames, Module.new { - def self.const_missing(name) - const_set(name, [name.to_s.sub(/ATTR_/, '')].pack('h*').freeze) - end - }) + super generated_feature_methods end @@ -155,7 +140,7 @@ module ActiveRecord else superclass.arel_engine end - end + end end private @@ -340,14 +325,6 @@ module ActiveRecord @readonly = true end - # Returns the connection currently associated with the class. This can - # also be used to "borrow" the connection to do database work that isn't - # easily done without going straight to SQL. - def connection - ActiveSupport::Deprecation.warn("#connection is deprecated in favour of accessing it via the class") - self.class.connection - end - def connection_handler self.class.connection_handler end diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index 70eda332b3..47d4f3637f 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -379,12 +379,6 @@ module ActiveRecord @@all_cached_fixtures = Hash.new { |h,k| h[k] = {} } - def self.find_table_name(fixture_set_name) # :nodoc: - ActiveSupport::Deprecation.warn( - "ActiveRecord::Fixtures.find_table_name is deprecated and shall be removed from future releases. Use ActiveRecord::Fixtures.default_fixture_model_name instead.") - default_fixture_model_name(fixture_set_name) - end - def self.default_fixture_model_name(fixture_set_name) # :nodoc: ActiveRecord::Base.pluralize_table_names ? fixture_set_name.singularize.camelize : diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index e96c347f6f..33ee129fc6 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -866,13 +866,7 @@ module ActiveRecord @direction = direction @target_version = target_version @migrated_versions = nil - - if Array(migrations).grep(String).empty? - @migrations = migrations - else - ActiveSupport::Deprecation.warn "instantiate this class with a list of migrations" - @migrations = self.class.migrations(migrations) - end + @migrations = migrations validate(@migrations) @@ -906,15 +900,7 @@ module ActiveRecord raise UnknownMigrationVersionError.new(@target_version) end - running = runnable - - if block_given? - message = "block argument to migrate is deprecated, please filter migrations before constructing the migrator" - ActiveSupport::Deprecation.warn message - running.select! { |m| yield m } - end - - running.each do |migration| + runnable.each do |migration| Base.logger.info "Migrating to #{migration.name} (#{migration.version})" if Base.logger begin diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index afb0be7b74..269f9f975b 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -111,56 +111,6 @@ module ActiveRecord initializer "active_record.set_configs" do |app| ActiveSupport.on_load(:active_record) do - begin - old_behavior, ActiveSupport::Deprecation.behavior = ActiveSupport::Deprecation.behavior, :stderr - whitelist_attributes = app.config.active_record.delete(:whitelist_attributes) - - if respond_to?(:mass_assignment_sanitizer=) - mass_assignment_sanitizer = nil - else - mass_assignment_sanitizer = app.config.active_record.delete(:mass_assignment_sanitizer) - end - - unless whitelist_attributes.nil? && mass_assignment_sanitizer.nil? - ActiveSupport::Deprecation.warn <<-EOF.strip_heredoc, [] - Model based mass assignment security has been extracted - out of Rails into a gem. Please use the new recommended protection model for - params or add `protected_attributes` to your Gemfile to use the old one. - - To disable this message remove the `whitelist_attributes` option from your - `config/application.rb` file and any `mass_assignment_sanitizer` options - from your `config/environments/*.rb` files. - - See http://guides.rubyonrails.org/security.html#mass-assignment for more information. - EOF - end - - unless app.config.active_record.delete(:auto_explain_threshold_in_seconds).nil? - ActiveSupport::Deprecation.warn <<-EOF.strip_heredoc, [] - The Active Record auto explain feature has been removed. - - To disable this message remove the `active_record.auto_explain_threshold_in_seconds` - option from the `config/environments/*.rb` config file. - - See http://guides.rubyonrails.org/4_0_release_notes.html for more information. - EOF - end - - unless app.config.active_record.delete(:observers).nil? - ActiveSupport::Deprecation.warn <<-EOF.strip_heredoc, [] - Active Record Observers has been extracted out of Rails into a gem. - Please use callbacks or add `rails-observers` to your Gemfile to use observers. - - To disable this message remove the `observers` option from your - `config/application.rb` or from your initializers. - - See http://guides.rubyonrails.org/4_0_release_notes.html for more information. - EOF - end - ensure - ActiveSupport::Deprecation.behavior = old_behavior - end - app.config.active_record.each do |k,v| send "#{k}=", v end diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 0b74553bf8..8a311039d5 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -12,7 +12,7 @@ db_namespace = namespace :db do end end - desc 'Create the database from DATABASE_URL or config/database.yml for the current Rails.env (use db:create:all to create all dbs in the config)' + desc 'Create the database from DATABASE_URL or config/database.yml for the current Rails.env (use db:create:all to create all databases in the config)' task :create => [:load_config] do if ENV['DATABASE_URL'] ActiveRecord::Tasks::DatabaseTasks.create_database_url @@ -172,7 +172,7 @@ db_namespace = namespace :db do end end - desc 'Create the database, load the schema, and initialize with the seed data (use db:reset to also drop the db first)' + desc 'Create the database, load the schema, and initialize with the seed data (use db:reset to also drop the database first)' task :setup => ['db:schema:load_if_ruby', 'db:structure:load_if_sql', :seed] desc 'Load the seed data from db/seeds.rb' @@ -236,7 +236,7 @@ db_namespace = namespace :db do end namespace :schema do - desc 'Create a db/schema.rb file that can be portably used against any DB supported by AR' + desc 'Create a db/schema.rb file that is portable against any DB supported by AR' task :dump => [:environment, :load_config] do require 'active_record/schema_dumper' filename = ENV['SCHEMA'] || File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, 'schema.rb') diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index d37471e9ad..4e86e905ed 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -17,17 +17,14 @@ module ActiveRecord include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches, Explain, Delegation attr_reader :table, :klass, :loaded - attr_accessor :default_scoped alias :model :klass alias :loaded? :loaded - alias :default_scoped? :default_scoped def initialize(klass, table, values = {}) - @klass = klass - @table = table - @values = values - @loaded = false - @default_scoped = false + @klass = klass + @table = table + @values = values + @loaded = false end def initialize_copy(other) @@ -313,7 +310,7 @@ module ActiveRecord stmt.table(table) stmt.key = table[primary_key] - if with_default_scope.joins_values.any? + if joins_values.any? @klass.connection.join_to_update(stmt, arel) else stmt.take(arel.limit) @@ -438,7 +435,7 @@ module ActiveRecord stmt = Arel::DeleteManager.new(arel.engine) stmt.from(table) - if with_default_scope.joins_values.any? + if joins_values.any? @klass.connection.join_to_delete(stmt, arel, table[primary_key]) else stmt.wheres = arel.constraints @@ -504,7 +501,22 @@ module ActiveRecord # User.where(name: 'Oscar').to_sql # # => SELECT "users".* FROM "users" WHERE "users"."name" = 'Oscar' def to_sql - @to_sql ||= klass.connection.to_sql(arel, bind_values.dup) + @to_sql ||= begin + relation = self + connection = klass.connection + visitor = connection.visitor + + if eager_loading? + join_dependency = construct_join_dependency + relation = construct_relation_for_association_find(join_dependency) + end + + ast = relation.arel.ast + binds = relation.bind_values.dup + visitor.accept(ast) do + connection.quote(*binds.shift.reverse) + end + end end # Returns a hash of where conditions. @@ -512,12 +524,11 @@ module ActiveRecord # User.where(name: 'Oscar').where_values_hash # # => {name: "Oscar"} def where_values_hash - scope = with_default_scope - equalities = scope.where_values.grep(Arel::Nodes::Equality).find_all { |node| + equalities = where_values.grep(Arel::Nodes::Equality).find_all { |node| node.left.relation.name == table_name } - binds = Hash[scope.bind_values.find_all(&:first).map { |column, v| [column.name, v] }] + binds = Hash[bind_values.find_all(&:first).map { |column, v| [column.name, v] }] binds.merge!(Hash[bind_values.find_all(&:first).map { |column, v| [column.name, v] }]) Hash[equalities.map { |where| @@ -565,16 +576,6 @@ module ActiveRecord q.pp(self.to_a) end - def with_default_scope #:nodoc: - if default_scoped? && default_scope = klass.send(:build_default_scope) - default_scope = default_scope.merge(self) - default_scope.default_scoped = false - default_scope - else - self - end - end - # Returns true if relation is blank. def blank? to_a.blank? @@ -594,31 +595,23 @@ module ActiveRecord private def exec_queries - default_scoped = with_default_scope - - if default_scoped.equal?(self) - @records = eager_loading? ? find_with_associations : @klass.find_by_sql(arel, bind_values) - - preload = preload_values - preload += includes_values unless eager_loading? - preload.each do |associations| - ActiveRecord::Associations::Preloader.new(@records, associations).run - end + @records = eager_loading? ? find_with_associations : @klass.find_by_sql(arel, bind_values) - @records.each { |record| record.readonly! } if readonly_value - else - @records = default_scoped.to_a + preload = preload_values + preload += includes_values unless eager_loading? + preload.each do |associations| + ActiveRecord::Associations::Preloader.new(@records, associations).run end + @records.each { |record| record.readonly! } if readonly_value + @loaded = true @records end def references_eager_loaded_tables? joined_tables = arel.join_sources.map do |join| - if join.is_a?(Arel::Nodes::StringJoin) - tables_in_string(join.left) - else + unless join.is_a?(Arel::Nodes::StringJoin) [join.left.table_name, join.left.table_alias] end end @@ -627,41 +620,8 @@ module ActiveRecord # always convert table names to downcase as in Oracle quoted table names are in uppercase joined_tables = joined_tables.flatten.compact.map { |t| t.downcase }.uniq - string_tables = tables_in_string(to_sql) - - if (references_values - joined_tables).any? - true - elsif !ActiveRecord::Base.disable_implicit_join_references && - (string_tables - joined_tables).any? - ActiveSupport::Deprecation.warn( - "It looks like you are eager loading table(s) (one of: #{string_tables.join(', ')}) " \ - "that are referenced in a string SQL snippet. For example: \n" \ - "\n" \ - " Post.includes(:comments).where(\"comments.title = 'foo'\")\n" \ - "\n" \ - "Currently, Active Record recognizes the table in the string, and knows to JOIN the " \ - "comments table to the query, rather than loading comments in a separate query. " \ - "However, doing this without writing a full-blown SQL parser is inherently flawed. " \ - "Since we don't want to write an SQL parser, we are removing this functionality. " \ - "From now on, you must explicitly tell Active Record when you are referencing a table " \ - "from a string:\n" \ - "\n" \ - " Post.includes(:comments).where(\"comments.title = 'foo'\").references(:comments)\n" \ - "\n" \ - "If you don't rely on implicit join references you can disable the feature entirely " \ - "by setting `config.active_record.disable_implicit_join_references = true`." - ) - true - else - false - end - end - def tables_in_string(string) - return [] if string.blank? - # always convert table names to downcase as in Oracle quoted table names are in uppercase - # ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries - string.scan(/([a-zA-Z_][.\w]+).?\./).flatten.map{ |s| s.downcase }.uniq - ['raw_sql_'] + (references_values - joined_tables).any? end end end diff --git a/activerecord/lib/active_record/relation/batches.rb b/activerecord/lib/active_record/relation/batches.rb index 91ea1756c4..fd8496442e 100644 --- a/activerecord/lib/active_record/relation/batches.rb +++ b/activerecord/lib/active_record/relation/batches.rb @@ -89,8 +89,8 @@ module ActiveRecord relation = self - unless arel.orders.blank? && arel.taken.blank? - ActiveRecord::Base.logger.warn("Scoped order and limit are ignored, it's forced to be batch order and batch size") + if logger && (arel.orders.present? || arel.taken.present?) + logger.warn("Scoped order and limit are ignored, it's forced to be batch order and batch size") end start = options.delete(:start) diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index 4becf3980d..3b24ed6754 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -91,20 +91,14 @@ module ActiveRecord # # Person.sum("2 * age") def calculate(operation, column_name, options = {}) - relation = with_default_scope - - if column_name.is_a?(Symbol) && attribute_aliases.key?(column_name.to_s) - column_name = attribute_aliases[column_name.to_s].to_sym + if column_name.is_a?(Symbol) && attribute_alias?(column_name) + column_name = attribute_alias(column_name) end - if relation.equal?(self) - if has_include?(column_name) - construct_relation_for_association_calculations.calculate(operation, column_name, options) - else - perform_calculation(operation, column_name, options) - end + if has_include?(column_name) + construct_relation_for_association_calculations.calculate(operation, column_name, options) else - relation.calculate(operation, column_name, options) + perform_calculation(operation, column_name, options) end end @@ -143,39 +137,32 @@ module ActiveRecord # def pluck(*column_names) column_names.map! do |column_name| - if column_name.is_a?(Symbol) - if attribute_aliases.key?(column_name.to_s) - column_name = attribute_aliases[column_name.to_s].to_sym - end - - if self.columns_hash.key?(column_name.to_s) - column_name = "#{connection.quote_table_name(table_name)}.#{connection.quote_column_name(column_name)}" - end + if column_name.is_a?(Symbol) && attribute_alias?(column_name) + attribute_alias(column_name) + else + column_name.to_s end - - column_name end if has_include?(column_names.first) construct_relation_for_association_calculations.pluck(*column_names) else relation = spawn - relation.select_values = column_names + relation.select_values = column_names.map { |cn| + columns_hash.key?(cn) ? arel_table[cn] : cn + } result = klass.connection.select_all(relation.arel, nil, bind_values) columns = result.columns.map do |key| klass.column_types.fetch(key) { - result.column_types.fetch(key) { - Class.new { def type_cast(v); v; end }.new - } + result.column_types.fetch(key) { result.identity_type } } end result = result.map do |attributes| values = klass.initialize_attributes(attributes).values - columns.zip(values).map do |column, value| - column.type_cast(value) - end + iter = columns.each + values.map { |value| iter.next.type_cast value } end columns.one? ? result.map!(&:first) : result end @@ -200,11 +187,6 @@ module ActiveRecord # If #count is used with #distinct / #uniq it is considered distinct. (eg. relation.distinct.count) distinct = self.distinct_value - if options.has_key?(:distinct) - ActiveSupport::Deprecation.warn "The :distinct option for `Relation#count` is deprecated. " \ - "Please use `Relation#distinct` instead. (eg. `relation.distinct.count`)" - distinct = options[:distinct] - end if operation == "count" if select_values.present? diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 3ea3c33fcc..9186b33bd2 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -1,5 +1,7 @@ module ActiveRecord module FinderMethods + ONE_AS_ONE = '1 AS one' + # Find by id - This can either be a specific id (1), a list of ids (1, 5, 6), or an array of ids ([5, 6, 10]). # If no record can be found for all of the listed ids, then RecordNotFound will be raised. If the primary key # is an integer, find by id coerces its arguments using +to_i+. @@ -32,7 +34,7 @@ module ActiveRecord # end # # ==== Variations of +find+ - # + # # Person.where(name: 'Spartacus', rating: 4) # # returns a chainable list (which can be empty). # @@ -49,7 +51,7 @@ module ActiveRecord # # Person.where(name: 'Spartacus', rating: 4).exists?(conditions = :none) # # returns a boolean indicating if any record with the given conditions exist. - # + # # Person.where(name: 'Spartacus', rating: 4).select("field1, field2, field3") # # returns a chainable list of instances with only the mentioned fields. # @@ -124,7 +126,7 @@ module ActiveRecord # def first(limit = nil) if limit - find_first_with_limit(order_values, limit) + find_first_with_limit(limit) else find_first end @@ -202,7 +204,7 @@ module ActiveRecord relation = construct_relation_for_association_find(construct_join_dependency) return false if ActiveRecord::NullRelation === relation - relation = relation.except(:select, :order).select("1 AS one").limit(1) + relation = relation.except(:select, :order).select(ONE_AS_ONE).limit(1) case conditions when Array, Hash @@ -211,7 +213,6 @@ module ActiveRecord relation = relation.where(table[primary_key].eq(conditions)) if conditions != :none end - relation = relation.with_default_scope connection.select_value(relation.arel, "#{name} Exists", relation.bind_values) end @@ -354,11 +355,11 @@ module ActiveRecord if loaded? @records.first else - @first ||= find_first_with_limit(with_default_scope.order_values, 1).first + @first ||= find_first_with_limit(1).first end end - def find_first_with_limit(order_values, limit) + def find_first_with_limit(limit) if order_values.empty? && primary_key order(arel_table[primary_key].asc).limit(limit).to_a else diff --git a/activerecord/lib/active_record/relation/merger.rb b/activerecord/lib/active_record/relation/merger.rb index c114ea0c0d..6d047e0331 100644 --- a/activerecord/lib/active_record/relation/merger.rb +++ b/activerecord/lib/active_record/relation/merger.rb @@ -42,10 +42,6 @@ module ActiveRecord attr_reader :relation, :values, :other def initialize(relation, other) - if other.default_scoped? && other.klass != relation.klass - other = other.with_default_scope - end - @relation = relation @values = other.values @other = other diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb index b7609c97b5..08ef899f68 100644 --- a/activerecord/lib/active_record/relation/predicate_builder.rb +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -1,15 +1,21 @@ module ActiveRecord class PredicateBuilder # :nodoc: + def self.resolve_column_aliases(klass, hash) + hash = hash.dup + hash.keys.grep(Symbol) do |key| + if klass.attribute_alias? key + hash[klass.attribute_alias(key)] = hash.delete key + end + end + hash + end + def self.build_from_hash(klass, attributes, default_table) queries = [] attributes.each do |column, value| table = default_table - if column.is_a?(Symbol) && klass.attribute_aliases.key?(column.to_s) - column = klass.attribute_aliases[column.to_s] - end - if value.is_a?(Hash) if value.empty? queries << '1=0' diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 0200fcf69b..1327cc3c34 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -795,7 +795,7 @@ module ActiveRecord # Returns the Arel object associated with the relation. def arel - @arel ||= with_default_scope.build_arel + @arel ||= build_arel end # Like #arel, but ignores the default scope of the model. @@ -891,6 +891,7 @@ module ActiveRecord when String, Array [@klass.send(:sanitize_sql, other.empty? ? opts : ([opts] + other))] when Hash + opts = PredicateBuilder.resolve_column_aliases klass, opts attributes = @klass.send(:expand_hash_conditions_for_aggregates, opts) attributes.values.grep(ActiveRecord::Relation) do |rel| diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb index de784f9f57..c63ae9c9fb 100644 --- a/activerecord/lib/active_record/relation/spawn_methods.rb +++ b/activerecord/lib/active_record/relation/spawn_methods.rb @@ -65,7 +65,6 @@ module ActiveRecord def relation_with(values) # :nodoc: result = Relation.new(klass, table, values) - result.default_scoped = default_scoped result.extend(*extending_values) if extending_values.any? result end diff --git a/activerecord/lib/active_record/result.rb b/activerecord/lib/active_record/result.rb index 6156b3a5ba..a7a035fe46 100644 --- a/activerecord/lib/active_record/result.rb +++ b/activerecord/lib/active_record/result.rb @@ -8,6 +8,8 @@ module ActiveRecord class Result include Enumerable + IDENTITY_TYPE = Class.new { def type_cast(v); v; end }.new # :nodoc: + attr_reader :columns, :rows, :column_types def initialize(columns, rows, column_types = {}) @@ -17,6 +19,10 @@ module ActiveRecord @column_types = column_types end + def identity_type # :nodoc: + IDENTITY_TYPE + end + def each if block_given? hash_rows.each { |row| yield row } diff --git a/activerecord/lib/active_record/sanitization.rb b/activerecord/lib/active_record/sanitization.rb index 0ed97b66d6..31e294022f 100644 --- a/activerecord/lib/active_record/sanitization.rb +++ b/activerecord/lib/active_record/sanitization.rb @@ -86,6 +86,7 @@ module ActiveRecord # { address: Address.new("123 abc st.", "chicago") } # # => "address_street='123 abc st.' and address_city='chicago'" def sanitize_sql_hash_for_conditions(attrs, default_table_name = self.table_name) + attrs = PredicateBuilder.resolve_column_aliases self, attrs attrs = expand_hash_conditions_for_aggregates(attrs) table = Arel::Table.new(table_name, arel_engine).alias(default_table_name) diff --git a/activerecord/lib/active_record/scoping/default.rb b/activerecord/lib/active_record/scoping/default.rb index d37d33d552..a5d6aad3f0 100644 --- a/activerecord/lib/active_record/scoping/default.rb +++ b/activerecord/lib/active_record/scoping/default.rb @@ -8,14 +8,6 @@ module ActiveRecord class_attribute :default_scopes, instance_writer: false, instance_predicate: false self.default_scopes = [] - - def self.default_scopes? - ActiveSupport::Deprecation.warn( - "#default_scopes? is deprecated. Do something like #default_scopes.empty? instead." - ) - - !!self.default_scopes - end end module ClassMethods @@ -91,12 +83,11 @@ module ActiveRecord scope = Proc.new if block_given? if scope.is_a?(Relation) || !scope.respond_to?(:call) - ActiveSupport::Deprecation.warn( - "Calling #default_scope without a block is deprecated. For example instead " \ + raise ArgumentError, + "Support for calling #default_scope without a block is removed. For example instead " \ "of `default_scope where(color: 'red')`, please use " \ "`default_scope { where(color: 'red') }`. (Alternatively you can just redefine " \ "self.default_scope.)" - ) end self.default_scopes += [scope] diff --git a/activerecord/lib/active_record/scoping/named.rb b/activerecord/lib/active_record/scoping/named.rb index da73bead32..7c51aa6979 100644 --- a/activerecord/lib/active_record/scoping/named.rb +++ b/activerecord/lib/active_record/scoping/named.rb @@ -25,22 +25,18 @@ module ActiveRecord if current_scope current_scope.clone else - scope = relation - scope.default_scoped = true - scope + default_scoped end end + def default_scoped # :nodoc: + relation.merge(build_default_scope) + end + # Collects attributes from scopes that should be applied when creating # an AR instance for the particular class this is called on. def scope_attributes # :nodoc: - if current_scope - current_scope.scope_for_create - else - scope = relation - scope.default_scoped = true - scope.scope_for_create - end + all.scope_for_create end # Are there default attributes associated with this scope? @@ -145,19 +141,6 @@ module ActiveRecord def scope(name, body, &block) extension = Module.new(&block) if block - # Check body.is_a?(Relation) to prevent the relation actually being - # loaded by respond_to? - if body.is_a?(Relation) || !body.respond_to?(:call) - ActiveSupport::Deprecation.warn( - "Using #scope without passing a callable object is deprecated. For " \ - "example `scope :red, where(color: 'red')` should be changed to " \ - "`scope :red, -> { where(color: 'red') }`. There are numerous gotchas " \ - "in the former usage and it makes the implementation more complicated " \ - "and buggy. (If you prefer, you can just define a class method named " \ - "`self.red`.)" - ) - end - singleton_class.send(:define_method, name) do |*args| if body.respond_to?(:call) scope = all.scoping { body.call(*args) } diff --git a/activerecord/lib/active_record/test_case.rb b/activerecord/lib/active_record/test_case.rb deleted file mode 100644 index 1b4c473bfc..0000000000 --- a/activerecord/lib/active_record/test_case.rb +++ /dev/null @@ -1,96 +0,0 @@ -require 'active_support/test_case' - -ActiveSupport::Deprecation.warn('ActiveRecord::TestCase is deprecated, please use ActiveSupport::TestCase') -module ActiveRecord - # = Active Record Test Case - # - # Defines some test assertions to test against SQL queries. - class TestCase < ActiveSupport::TestCase #:nodoc: - def teardown - SQLCounter.clear_log - end - - def assert_date_from_db(expected, actual, message = nil) - # SybaseAdapter doesn't have a separate column type just for dates, - # so the time is in the string and incorrectly formatted - if current_adapter?(:SybaseAdapter) - assert_equal expected.to_s, actual.to_date.to_s, message - else - assert_equal expected.to_s, actual.to_s, message - end - end - - def assert_sql(*patterns_to_match) - SQLCounter.clear_log - yield - SQLCounter.log_all - ensure - failed_patterns = [] - patterns_to_match.each do |pattern| - failed_patterns << pattern unless SQLCounter.log_all.any?{ |sql| pattern === sql } - end - assert failed_patterns.empty?, "Query pattern(s) #{failed_patterns.map{ |p| p.inspect }.join(', ')} not found.#{SQLCounter.log.size == 0 ? '' : "\nQueries:\n#{SQLCounter.log.join("\n")}"}" - end - - def assert_queries(num = 1, options = {}) - ignore_none = options.fetch(:ignore_none) { num == :any } - SQLCounter.clear_log - x = yield - the_log = ignore_none ? SQLCounter.log_all : SQLCounter.log - if num == :any - assert_operator the_log.size, :>=, 1, "1 or more queries expected, but none were executed." - else - mesg = "#{the_log.size} instead of #{num} queries were executed.#{the_log.size == 0 ? '' : "\nQueries:\n#{the_log.join("\n")}"}" - assert_equal num, the_log.size, mesg - end - x - end - - def assert_no_queries(&block) - assert_queries(0, :ignore_none => true, &block) - end - - end - - class SQLCounter - class << self - attr_accessor :ignored_sql, :log, :log_all - def clear_log; self.log = []; self.log_all = []; end - end - - self.clear_log - - self.ignored_sql = [/^PRAGMA/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^SAVEPOINT/, /^ROLLBACK TO SAVEPOINT/, /^RELEASE SAVEPOINT/, /^SHOW max_identifier_length/, /^BEGIN/, /^COMMIT/] - - # FIXME: this needs to be refactored so specific database can add their own - # ignored SQL, or better yet, use a different notification for the queries - # instead examining the SQL content. - oracle_ignored = [/^select .*nextval/i, /^SAVEPOINT/, /^ROLLBACK TO/, /^\s*select .* from all_triggers/im] - mysql_ignored = [/^SHOW TABLES/i, /^SHOW FULL FIELDS/] - postgresql_ignored = [/^\s*select\b.*\bfrom\b.*pg_namespace\b/im, /^\s*select\b.*\battname\b.*\bfrom\b.*\bpg_attribute\b/im, /^SHOW search_path/i] - sqlite3_ignored = [/^\s*SELECT name\b.*\bFROM sqlite_master/im] - - [oracle_ignored, mysql_ignored, postgresql_ignored, sqlite3_ignored].each do |db_ignored_sql| - ignored_sql.concat db_ignored_sql - end - - attr_reader :ignore - - def initialize(ignore = Regexp.union(self.class.ignored_sql)) - @ignore = ignore - end - - def call(name, start, finish, message_id, values) - sql = values[:sql] - - # FIXME: this seems bad. we should probably have a better way to indicate - # the query was cached - return if 'CACHE' == values[:name] - - self.class.log_all << sql - self.class.log << sql unless ignore =~ sql - end - end - - ActiveSupport::Notifications.subscribe('sql.active_record', SQLCounter.new) -end diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb index 52e46e1ffe..b55af692d6 100644 --- a/activerecord/lib/active_record/validations/uniqueness.rb +++ b/activerecord/lib/active_record/validations/uniqueness.rb @@ -197,8 +197,8 @@ module ActiveRecord # will result in the default Rails exception page being shown), or you # can catch it and restart the transaction (e.g. by telling the user # that the title already exists, and asking him to re-enter the title). - # This technique is also known as optimistic concurrency control: - # http://en.wikipedia.org/wiki/Optimistic_concurrency_control. + # This technique is also known as + # {optimistic concurrency control}[http://en.wikipedia.org/wiki/Optimistic_concurrency_control]. # # The bundled ActiveRecord::ConnectionAdapters distinguish unique index # constraint errors from other types of database errors by throwing an diff --git a/activerecord/test/cases/adapters/postgresql/json_test.rb b/activerecord/test/cases/adapters/postgresql/json_test.rb index f45c7afcc0..adac1d3c13 100644 --- a/activerecord/test/cases/adapters/postgresql/json_test.rb +++ b/activerecord/test/cases/adapters/postgresql/json_test.rb @@ -96,5 +96,4 @@ class PostgresqlJSONTest < ActiveRecord::TestCase x.payload = ['v1', {'k2' => 'v2'}, 'v3'] assert x.save! end - end diff --git a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb index fb88ab7c09..8b017760b1 100644 --- a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb +++ b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb @@ -225,65 +225,26 @@ module ActiveRecord assert_equal "(number > 100)", index.where end - def test_distinct_zero_orders - assert_deprecated do - assert_equal "DISTINCT posts.id", - @connection.distinct("posts.id", []) - end - end - def test_columns_for_distinct_zero_orders assert_equal "posts.id", @connection.columns_for_distinct("posts.id", []) end - def test_distinct_one_order - assert_deprecated do - assert_equal "DISTINCT posts.id, posts.created_at AS alias_0", - @connection.distinct("posts.id", ["posts.created_at desc"]) - end - end - def test_columns_for_distinct_one_order assert_equal "posts.id, posts.created_at AS alias_0", @connection.columns_for_distinct("posts.id", ["posts.created_at desc"]) end - def test_distinct_few_orders - assert_deprecated do - assert_equal "DISTINCT posts.id, posts.created_at AS alias_0, posts.position AS alias_1", - @connection.distinct("posts.id", ["posts.created_at desc", "posts.position asc"]) - end - end - def test_columns_for_distinct_few_orders assert_equal "posts.id, posts.created_at AS alias_0, posts.position AS alias_1", @connection.columns_for_distinct("posts.id", ["posts.created_at desc", "posts.position asc"]) end - def test_distinct_blank_not_nil_orders - assert_deprecated do - assert_equal "DISTINCT posts.id, posts.created_at AS alias_0", - @connection.distinct("posts.id", ["posts.created_at desc", "", " "]) - end - end - def test_columns_for_distinct_blank_not_nil_orders assert_equal "posts.id, posts.created_at AS alias_0", @connection.columns_for_distinct("posts.id", ["posts.created_at desc", "", " "]) end - def test_distinct_with_arel_order - order = Object.new - def order.to_sql - "posts.created_at desc" - end - assert_deprecated do - assert_equal "DISTINCT posts.id, posts.created_at AS alias_0", - @connection.distinct("posts.id", [order]) - end - end - def test_columns_for_distinct_with_arel_order order = Object.new def order.to_sql @@ -293,13 +254,6 @@ module ActiveRecord @connection.columns_for_distinct("posts.id", [order]) end - def test_distinct_with_nulls - assert_deprecated do - assert_equal "DISTINCT posts.title, posts.updater_id AS alias_0", @connection.distinct("posts.title", ["posts.updater_id desc nulls first"]) - assert_equal "DISTINCT posts.title, posts.updater_id AS alias_0", @connection.distinct("posts.title", ["posts.updater_id desc nulls last"]) - end - end - def test_columns_for_distinct_with_nulls assert_equal "posts.title, posts.updater_id AS alias_0", @connection.columns_for_distinct("posts.title", ["posts.updater_id desc nulls first"]) assert_equal "posts.title, posts.updater_id AS alias_0", @connection.columns_for_distinct("posts.title", ["posts.updater_id desc nulls last"]) diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb index 95896971a8..0267cdf6e0 100644 --- a/activerecord/test/cases/associations/belongs_to_associations_test.rb +++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb @@ -1,4 +1,4 @@ -require "cases/helper" +require 'cases/helper' require 'models/developer' require 'models/project' require 'models/company' @@ -14,6 +14,8 @@ require 'models/sponsor' require 'models/member' require 'models/essay' require 'models/toy' +require 'models/invoice' +require 'models/line_item' class BelongsToAssociationsTest < ActiveRecord::TestCase fixtures :accounts, :companies, :developers, :projects, :topics, @@ -324,6 +326,45 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase assert_equal 1, Topic.find(topic.id)[:replies_count] end + def test_belongs_to_with_touch_option_on_touch + line_item = LineItem.create! + Invoice.create!(line_items: [line_item]) + + assert_queries(1) { line_item.touch } + end + + def test_belongs_to_with_touch_option_on_touch_and_removed_parent + line_item = LineItem.create! + Invoice.create!(line_items: [line_item]) + + line_item.invoice = nil + + assert_queries(2) { line_item.touch } + end + + def test_belongs_to_with_touch_option_on_update + line_item = LineItem.create! + Invoice.create!(line_items: [line_item]) + + assert_queries(2) { line_item.update amount: 10 } + end + + def test_belongs_to_with_touch_option_on_destroy + line_item = LineItem.create! + Invoice.create!(line_items: [line_item]) + + assert_queries(2) { line_item.destroy } + end + + def test_belongs_to_with_touch_option_on_touch_and_reassigned_parent + line_item = LineItem.create! + Invoice.create!(line_items: [line_item]) + + line_item.invoice = Invoice.create! + + assert_queries(3) { line_item.touch } + end + def test_belongs_to_counter_after_update topic = Topic.create!(title: "37s") topic.replies.create!(title: "re: 37s", content: "rails") @@ -391,8 +432,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase def test_dont_find_target_when_foreign_key_is_null tagging = taggings(:thinking_general) - queries = assert_sql { tagging.super_tag } - assert_equal 0, queries.length + assert_queries(0) { tagging.super_tag } end def test_field_name_same_as_foreign_key diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb index 1cfaf552af..28bf48f4fd 100644 --- a/activerecord/test/cases/associations/eager_test.rb +++ b/activerecord/test/cases/associations/eager_test.rb @@ -346,9 +346,7 @@ class EagerAssociationTest < ActiveRecord::TestCase def test_eager_association_loading_with_belongs_to_and_conditions_string_with_unquoted_table_name assert_nothing_raised do - ActiveSupport::Deprecation.silence do - Comment.all.merge!(:includes => :post, :where => ['posts.id = ?',4]).to_a - end + Comment.includes(:post).references(:posts).where('posts.id = ?', 4) end end @@ -367,9 +365,7 @@ class EagerAssociationTest < ActiveRecord::TestCase def test_eager_association_loading_with_belongs_to_and_conditions_string_with_quoted_table_name quoted_posts_id= Comment.connection.quote_table_name('posts') + '.' + Comment.connection.quote_column_name('id') assert_nothing_raised do - ActiveSupport::Deprecation.silence do - Comment.all.merge!(:includes => :post, :where => ["#{quoted_posts_id} = ?",4]).to_a - end + Comment.includes(:post).references(:posts).where("#{quoted_posts_id} = ?", 4) end end @@ -382,9 +378,7 @@ class EagerAssociationTest < ActiveRecord::TestCase def test_eager_association_loading_with_belongs_to_and_order_string_with_quoted_table_name quoted_posts_id= Comment.connection.quote_table_name('posts') + '.' + Comment.connection.quote_column_name('id') assert_nothing_raised do - ActiveSupport::Deprecation.silence do - Comment.all.merge!(:includes => :post, :order => quoted_posts_id).to_a - end + Comment.includes(:post).references(:posts).order(quoted_posts_id) end end @@ -548,15 +542,11 @@ class EagerAssociationTest < ActiveRecord::TestCase end def test_eager_with_has_many_and_limit_and_conditions_array_on_the_eagers - posts = ActiveSupport::Deprecation.silence do - Post.all.merge!(:includes => [ :author, :comments ], :limit => 2, :where => [ "authors.name = ?", 'David' ]).to_a - end + posts = Post.includes(:author, :comments).limit(2).references(:author).where("authors.name = ?", 'David') assert_equal 2, posts.size - count = ActiveSupport::Deprecation.silence do - Post.count(:include => [ :author, :comments ], :limit => 2, :conditions => [ "authors.name = ?", 'David' ]) - end - assert_equal count, posts.size + count = Post.includes(:author, :comments).limit(2).references(:author).where("authors.name = ?", 'David').count + assert_equal posts.size, count end def test_eager_with_has_many_and_limit_and_high_offset diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb index 84bdca3a97..c63f48e370 100644 --- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb @@ -65,19 +65,6 @@ class DeveloperWithSymbolsForKeys < ActiveRecord::Base :foreign_key => "developer_id" end -class DeveloperWithCounterSQL < ActiveRecord::Base - self.table_name = 'developers' - - ActiveSupport::Deprecation.silence do - has_and_belongs_to_many :projects, - :class_name => "DeveloperWithCounterSQL", - :join_table => "developers_projects", - :association_foreign_key => "project_id", - :foreign_key => "developer_id", - :counter_sql => proc { "SELECT COUNT(*) AS count_all FROM projects INNER JOIN developers_projects ON projects.id = developers_projects.project_id WHERE developers_projects.developer_id =#{id}" } - end -end - class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase fixtures :accounts, :companies, :categories, :posts, :categories_posts, :developers, :projects, :developers_projects, :parrots, :pirates, :parrots_pirates, :treasures, :price_estimates, :tags, :taggings @@ -364,31 +351,6 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase assert_equal 0, david.projects(true).size end - def test_deleting_with_sql - david = Developer.find(1) - active_record = Project.find(1) - active_record.developers.reload - assert_equal 3, active_record.developers_by_sql.size - - active_record.developers_by_sql.delete(david) - assert_equal 2, active_record.developers_by_sql(true).size - end - - def test_deleting_array_with_sql - active_record = Project.find(1) - active_record.developers.reload - assert_equal 3, active_record.developers_by_sql.size - - active_record.developers_by_sql.delete(Developer.all) - assert_equal 0, active_record.developers_by_sql(true).size - end - - def test_deleting_all_with_sql - project = Project.find(1) - project.developers_by_sql.delete_all - assert_equal 0, project.developers_by_sql.size - end - def test_deleting_all david = Developer.find(1) david.projects.reload @@ -475,13 +437,6 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase assert george.treasures(true).empty? end - def test_deprecated_push_with_attributes_was_removed - jamis = developers(:jamis) - assert_raise(NoMethodError) do - jamis.projects.push_with_attributes(projects(:action_controller), :joined_on => Date.today) - end - end - def test_associations_with_conditions assert_equal 3, projects(:active_record).developers.size assert_equal 1, projects(:active_record).developers_named_david.size @@ -537,25 +492,6 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase assert ! project.developers.include?(developer) end - def test_find_in_association_with_custom_finder_sql - assert_equal developers(:david), projects(:active_record).developers_with_finder_sql.find(developers(:david).id), "SQL find" - - active_record = projects(:active_record) - active_record.developers_with_finder_sql.reload - assert_equal developers(:david), active_record.developers_with_finder_sql.find(developers(:david).id), "Ruby find" - end - - def test_find_in_association_with_custom_finder_sql_and_multiple_interpolations - # interpolate once: - assert_equal [developers(:david), developers(:jamis), developers(:poor_jamis)], projects(:active_record).developers_with_finder_sql, "first interpolation" - # interpolate again, for a different project id - assert_equal [developers(:david)], projects(:action_controller).developers_with_finder_sql, "second interpolation" - end - - def test_find_in_association_with_custom_finder_sql_and_string_id - assert_equal developers(:david), projects(:active_record).developers_with_finder_sql.find(developers(:david).id.to_s), "SQL find" - end - def test_find_with_merged_options assert_equal 1, projects(:active_record).limited_developers.size assert_equal 1, projects(:active_record).limited_developers.to_a.size @@ -791,21 +727,6 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase assert_equal 2, david.projects.count end - def test_count_with_counter_sql - developer = DeveloperWithCounterSQL.create(:name => 'tekin') - developer.project_ids = [projects(:active_record).id] - developer.save - developer.reload - assert_equal 1, developer.projects.count - end - - unless current_adapter?(:PostgreSQLAdapter) - def test_count_with_finder_sql - assert_equal 3, projects(:active_record).developers_with_finder_sql.count - assert_equal 3, projects(:active_record).developers_with_multiline_finder_sql.count - end - end - def test_association_proxy_transaction_method_starts_transaction_in_association_class Post.expects(:transaction) Category.first.posts.transaction do @@ -845,18 +766,6 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase assert project.developers.include?(developer) end - test ":insert_sql is deprecated" do - klass = Class.new(ActiveRecord::Base) - def klass.name; 'Foo'; end - assert_deprecated { klass.has_and_belongs_to_many :posts, :insert_sql => 'lol' } - end - - test ":delete_sql is deprecated" do - klass = Class.new(ActiveRecord::Base) - def klass.name; 'Foo'; end - assert_deprecated { klass.has_and_belongs_to_many :posts, :delete_sql => 'lol' } - end - test "has and belongs to many associations on new records use null relations" do projects = Developer.new.projects assert_no_queries do diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 8f5e18b863..9c484a8bbe 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -23,79 +23,6 @@ require 'models/categorization' require 'models/minivan' require 'models/speedometer' -class HasManyAssociationsTestForCountWithFinderSql < ActiveRecord::TestCase - class Invoice < ActiveRecord::Base - ActiveSupport::Deprecation.silence do - has_many :custom_line_items, :class_name => 'LineItem', :finder_sql => "SELECT line_items.* from line_items" - end - end - def test_should_fail - assert_raise(ArgumentError) do - Invoice.create.custom_line_items.count(:conditions => {:amount => 0}) - end - end -end - -class HasManyAssociationsTestForCountWithCountSql < ActiveRecord::TestCase - class Invoice < ActiveRecord::Base - ActiveSupport::Deprecation.silence do - has_many :custom_line_items, :class_name => 'LineItem', :counter_sql => "SELECT COUNT(*) line_items.* from line_items" - end - end - def test_should_fail - assert_raise(ArgumentError) do - Invoice.create.custom_line_items.count(:conditions => {:amount => 0}) - end - end -end - -class HasManyAssociationsTestForCountWithVariousFinderSqls < ActiveRecord::TestCase - class Invoice < ActiveRecord::Base - ActiveSupport::Deprecation.silence do - has_many :custom_line_items, :class_name => 'LineItem', :finder_sql => "SELECT DISTINCT line_items.amount from line_items" - has_many :custom_full_line_items, :class_name => 'LineItem', :finder_sql => "SELECT line_items.invoice_id, line_items.amount from line_items" - has_many :custom_star_line_items, :class_name => 'LineItem', :finder_sql => "SELECT * from line_items" - has_many :custom_qualified_star_line_items, :class_name => 'LineItem', :finder_sql => "SELECT line_items.* from line_items" - end - end - - def test_should_count_distinct_results - invoice = Invoice.new - invoice.custom_line_items << LineItem.new(:amount => 0) - invoice.custom_line_items << LineItem.new(:amount => 0) - invoice.save! - - assert_equal 1, invoice.custom_line_items.count - end - - def test_should_count_results_with_multiple_fields - invoice = Invoice.new - invoice.custom_full_line_items << LineItem.new(:amount => 0) - invoice.custom_full_line_items << LineItem.new(:amount => 0) - invoice.save! - - assert_equal 2, invoice.custom_full_line_items.count - end - - def test_should_count_results_with_star - invoice = Invoice.new - invoice.custom_star_line_items << LineItem.new(:amount => 0) - invoice.custom_star_line_items << LineItem.new(:amount => 0) - invoice.save! - - assert_equal 2, invoice.custom_star_line_items.count - end - - def test_should_count_results_with_qualified_star - invoice = Invoice.new - invoice.custom_qualified_star_line_items << LineItem.new(:amount => 0) - invoice.custom_qualified_star_line_items << LineItem.new(:amount => 0) - invoice.save! - - assert_equal 2, invoice.custom_qualified_star_line_items.count - end -end - class HasManyAssociationsTestForReorderWithJoinDependency < ActiveRecord::TestCase fixtures :authors, :posts, :comments @@ -148,6 +75,14 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert_equal 'defaulty', bulb.name end + def test_do_not_call_callbacks_for_delete_all + bulb_count = Bulb.count + car = Car.create(:name => 'honda') + car.funky_bulbs.create! + assert_nothing_raised { car.reload.funky_bulbs.delete_all } + assert_equal bulb_count + 1, Bulb.count, "bulbs should have been deleted using :nullify strategey" + end + def test_building_the_associated_object_with_implicit_sti_base_class firm = DependentFirm.new company = firm.companies.build @@ -357,37 +292,6 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert_equal "Summit", Firm.all.merge!(:order => "id").first.clients_using_primary_key.first.name end - def test_finding_using_sql - firm = Firm.order("id").first - first_client = firm.clients_using_sql.first - assert_not_nil first_client - assert_equal "Microsoft", first_client.name - assert_equal 1, firm.clients_using_sql.size - assert_equal 1, Firm.order("id").first.clients_using_sql.size - end - - def test_finding_using_sql_take_into_account_only_uniq_ids - firm = Firm.order("id").first - client = firm.clients_using_sql.first - assert_equal client, firm.clients_using_sql.find(client.id, client.id) - assert_equal client, firm.clients_using_sql.find(client.id, client.id.to_s) - end - - def test_counting_using_sql - assert_equal 1, Firm.order("id").first.clients_using_counter_sql.size - assert Firm.order("id").first.clients_using_counter_sql.any? - assert_equal 0, Firm.order("id").first.clients_using_zero_counter_sql.size - assert !Firm.order("id").first.clients_using_zero_counter_sql.any? - end - - def test_counting_non_existant_items_using_sql - assert_equal 0, Firm.order("id").first.no_clients_using_counter_sql.size - end - - def test_counting_using_finder_sql - assert_equal 2, Firm.find(4).clients_using_sql.count - end - def test_belongs_to_sanity c = Client.new assert_nil c.firm @@ -415,22 +319,6 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert_raise(ActiveRecord::RecordNotFound) { firm.clients.find(2, 99) } end - def test_find_string_ids_when_using_finder_sql - firm = Firm.order("id").first - - client = firm.clients_using_finder_sql.find("2") - assert_kind_of Client, client - - client_ary = firm.clients_using_finder_sql.find(["2"]) - assert_kind_of Array, client_ary - assert_equal client, client_ary.first - - client_ary = firm.clients_using_finder_sql.find("2", "3") - assert_kind_of Array, client_ary - assert_equal 2, client_ary.size - assert_equal true, client_ary.include?(client) - end - def test_find_all firm = Firm.all.merge!(:order => "id").first assert_equal 2, firm.clients.where("#{QUOTED_TYPE} = 'Client'").to_a.length @@ -930,18 +818,33 @@ class HasManyAssociationsTest < ActiveRecord::TestCase firm = companies(:first_firm) client_id = firm.dependent_clients_of_firm.first.id assert_equal 1, firm.dependent_clients_of_firm.size + assert_equal 1, Client.find_by_id(client_id).client_of - # :dependent means destroy is called on each client + # :nullify is called on each client firm.dependent_clients_of_firm.clear assert_equal 0, firm.dependent_clients_of_firm.size assert_equal 0, firm.dependent_clients_of_firm(true).size - assert_equal [client_id], Client.destroyed_client_ids[firm.id] + assert_equal [], Client.destroyed_client_ids[firm.id] # Should be destroyed since the association is dependent. + assert_nil Client.find_by_id(client_id).client_of + end + + def test_delete_all_with_option_delete_all + firm = companies(:first_firm) + client_id = firm.dependent_clients_of_firm.first.id + firm.dependent_clients_of_firm.delete_all(:delete_all) assert_nil Client.find_by_id(client_id) end + def test_delete_all_accepts_limited_parameters + firm = companies(:first_firm) + assert_raise(ArgumentError) do + firm.dependent_clients_of_firm.delete_all(:destroy) + end + end + def test_clearing_an_exclusively_dependent_association_collection firm = companies(:first_firm) client_id = firm.exclusively_dependent_clients_of_firm.first.id @@ -1189,21 +1092,6 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert_equal num_accounts, Account.count end - def test_restrict - firm = RestrictedFirm.create!(:name => 'restrict') - firm.companies.create(:name => 'child') - - assert !firm.companies.empty? - assert_raise(ActiveRecord::DeleteRestrictionError) { firm.destroy } - assert RestrictedFirm.exists?(:name => 'restrict') - assert firm.companies.exists?(:name => 'child') - end - - def test_restrict_is_deprecated - klass = Class.new(ActiveRecord::Base) - assert_deprecated { klass.has_many :posts, dependent: :restrict } - end - def test_restrict_with_exception firm = RestrictedWithExceptionFirm.create!(:name => 'restrict') firm.companies.create(:name => 'child') @@ -1325,13 +1213,6 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert_equal [readers(:michael_welcome).id], posts(:welcome).readers_with_person_ids end - def test_get_ids_for_unloaded_finder_sql_associations_loads_them - company = companies(:first_firm) - assert !company.clients_using_sql.loaded? - assert_equal [companies(:second_client).id], company.clients_using_sql_ids - assert company.clients_using_sql.loaded? - end - def test_get_ids_for_ordered_association assert_equal [companies(:second_client).id, companies(:first_client).id], companies(:first_firm).clients_ordered_by_name_ids end @@ -1419,17 +1300,6 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert ! firm.clients.loaded? end - def test_include_loads_collection_if_target_uses_finder_sql - firm = companies(:first_firm) - client = firm.clients_using_sql.first - - firm.reload - assert ! firm.clients_using_sql.loaded? - assert_equal true, firm.clients_using_sql.include?(client) - assert firm.clients_using_sql.loaded? - end - - def test_include_returns_false_for_non_matching_record_to_verify_scoping firm = companies(:first_firm) client = Client.create!(:name => 'Not Associated') @@ -1774,16 +1644,6 @@ class HasManyAssociationsTest < ActiveRecord::TestCase end end - test ":finder_sql is deprecated" do - klass = Class.new(ActiveRecord::Base) - assert_deprecated { klass.has_many :foo, :finder_sql => 'lol' } - end - - test ":counter_sql is deprecated" do - klass = Class.new(ActiveRecord::Base) - assert_deprecated { klass.has_many :foo, :counter_sql => 'lol' } - end - test "has many associations on new records use null relations" do post = Post.new diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index 70c6b489aa..ee88fd490a 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -57,6 +57,40 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase assert post.reload.people(true).include?(person) end + def test_delete_all_for_with_dependent_option_destroy + person = people(:david) + assert_equal 1, person.jobs_with_dependent_destroy.count + + assert_no_difference 'Job.count' do + assert_difference 'Reference.count', -1 do + person.reload.jobs_with_dependent_destroy.delete_all + end + end + end + + def test_delete_all_for_with_dependent_option_nullify + person = people(:david) + assert_equal 1, person.jobs_with_dependent_nullify.count + + assert_no_difference 'Job.count' do + assert_no_difference 'Reference.count' do + person.reload.jobs_with_dependent_nullify.delete_all + end + end + end + + def test_delete_all_for_with_dependent_option_delete_all + person = people(:david) + assert_equal 1, person.jobs_with_dependent_delete_all.count + + assert_no_difference 'Job.count' do + assert_difference 'Reference.count', -1 do + person.reload.jobs_with_dependent_delete_all.delete_all + end + end + end + + def test_associate_existing_record_twice_should_add_to_target_twice post = posts(:thinking) person = people(:david) diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb index 0e48fbca9c..4fdf9a9643 100644 --- a/activerecord/test/cases/associations/has_one_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_associations_test.rb @@ -158,22 +158,6 @@ class HasOneAssociationsTest < ActiveRecord::TestCase assert_nothing_raised { firm.destroy } end - def test_restrict - firm = RestrictedFirm.create!(:name => 'restrict') - firm.create_account(:credit_limit => 10) - - assert_not_nil firm.account - - assert_raise(ActiveRecord::DeleteRestrictionError) { firm.destroy } - assert RestrictedFirm.exists?(:name => 'restrict') - assert firm.account.present? - end - - def test_restrict_is_deprecated - klass = Class.new(ActiveRecord::Base) - assert_deprecated { klass.has_one :post, dependent: :restrict } - end - def test_restrict_with_exception firm = RestrictedWithExceptionFirm.create!(:name => 'restrict') firm.create_account(:credit_limit => 10) diff --git a/activerecord/test/cases/associations/inner_join_association_test.rb b/activerecord/test/cases/associations/inner_join_association_test.rb index 9baf94399a..de47a576c6 100644 --- a/activerecord/test/cases/associations/inner_join_association_test.rb +++ b/activerecord/test/cases/associations/inner_join_association_test.rb @@ -104,4 +104,12 @@ class InnerJoinAssociationTest < ActiveRecord::TestCase assert !posts(:welcome).tags.empty? assert Post.joins(:misc_tags).where(:id => posts(:welcome).id).empty? end + + test "the default scope of the target is applied when joining associations" do + author = Author.create! name: "Jon" + author.categorizations.create! + author.categorizations.create! special: true + + assert_equal [author], Author.where(id: author).joins(:special_categorizations) + end end diff --git a/activerecord/test/cases/attribute_methods/read_test.rb b/activerecord/test/cases/attribute_methods/read_test.rb index 8d8ff2f952..c0659fddef 100644 --- a/activerecord/test/cases/attribute_methods/read_test.rb +++ b/activerecord/test/cases/attribute_methods/read_test.rb @@ -15,13 +15,6 @@ module ActiveRecord include ActiveRecord::AttributeMethods - def self.define_attribute_methods - # Created in the inherited/included hook for "proper" ARs - @attribute_methods_mutex ||= Mutex.new - - super - end - def self.column_names %w{ one two three } end @@ -56,9 +49,9 @@ module ActiveRecord end def test_attribute_methods_generated? - assert(!@klass.attribute_methods_generated?, 'attribute_methods_generated?') + assert_not @klass.method_defined?(:one) @klass.define_attribute_methods - assert(@klass.attribute_methods_generated?, 'attribute_methods_generated?') + assert @klass.method_defined?(:one) end end end diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 395f28f280..3a1830a739 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -1,4 +1,5 @@ require "cases/helper" +require 'active_support/concurrency/latch' require 'models/post' require 'models/author' require 'models/topic' @@ -1458,21 +1459,20 @@ class BasicsTest < ActiveRecord::TestCase orig_handler = klass.connection_handler new_handler = ActiveRecord::ConnectionAdapters::ConnectionHandler.new after_handler = nil - is_set = false + latch1 = ActiveSupport::Concurrency::Latch.new + latch2 = ActiveSupport::Concurrency::Latch.new t = Thread.new do klass.connection_handler = new_handler - is_set = true - Thread.stop + latch1.release + latch2.await after_handler = klass.connection_handler end - while(!is_set) - Thread.pass - end + latch1.await klass.connection_handler = orig_handler - t.wakeup + latch2.release t.join assert_equal after_handler, new_handler diff --git a/activerecord/test/cases/batches_test.rb b/activerecord/test/cases/batches_test.rb index 1d6676da1c..38c2560d69 100644 --- a/activerecord/test/cases/batches_test.rb +++ b/activerecord/test/cases/batches_test.rb @@ -68,6 +68,16 @@ class EachTest < ActiveRecord::TestCase Post.order("title").find_each { |post| post } end + def test_logger_not_required + previous_logger = ActiveRecord::Base.logger + ActiveRecord::Base.logger = nil + assert_nothing_raised do + Post.limit(1).find_each { |post| post } + end + ensure + ActiveRecord::Base.logger = previous_logger + end + def test_find_in_batches_should_return_batches assert_queries(@total + 1) do Post.find_in_batches(:batch_size => 1) do |batch| diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb index 0f3f9aecfc..73a183f9b4 100644 --- a/activerecord/test/cases/calculations_test.rb +++ b/activerecord/test/cases/calculations_test.rb @@ -351,16 +351,6 @@ class CalculationsTest < ActiveRecord::TestCase assert_equal 5, Account.count(:firm_id) end - def test_count_distinct_option_is_deprecated - assert_deprecated do - assert_equal 4, Account.select(:credit_limit).count(distinct: true) - end - - assert_deprecated do - assert_equal 6, Account.select(:credit_limit).count(distinct: false) - end - end - def test_count_with_distinct assert_equal 4, Account.select(:credit_limit).distinct.count assert_equal 4, Account.select(:credit_limit).uniq.count diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb index d5365b695e..2da51ea015 100644 --- a/activerecord/test/cases/connection_pool_test.rb +++ b/activerecord/test/cases/connection_pool_test.rb @@ -118,6 +118,7 @@ module ActiveRecord connection = cs.first @pool.remove connection assert_respond_to t.join.value, :execute + connection.close end def test_reap_and_active diff --git a/activerecord/test/cases/deprecated_dynamic_methods_test.rb b/activerecord/test/cases/deprecated_dynamic_methods_test.rb deleted file mode 100644 index 8e842d8758..0000000000 --- a/activerecord/test/cases/deprecated_dynamic_methods_test.rb +++ /dev/null @@ -1,592 +0,0 @@ -# This file should be deleted when activerecord-deprecated_finders is removed as -# a dependency. -# -# It is kept for now as there is some fairly nuanced behavior in the dynamic -# finders so it is useful to keep this around to guard against regressions if -# we need to change the code. - -require 'cases/helper' -require 'models/topic' -require 'models/reply' -require 'models/customer' -require 'models/post' -require 'models/company' -require 'models/author' -require 'models/category' -require 'models/comment' -require 'models/person' -require 'models/reader' - -class DeprecatedDynamicMethodsTest < ActiveRecord::TestCase - fixtures :topics, :customers, :companies, :accounts, :posts, :categories, :categories_posts, :authors, :people, :comments, :readers - - def setup - @deprecation_behavior = ActiveSupport::Deprecation.behavior - ActiveSupport::Deprecation.behavior = :silence - end - - def teardown - ActiveSupport::Deprecation.behavior = @deprecation_behavior - end - - def test_find_all_by_one_attribute - topics = Topic.find_all_by_content("Have a nice day") - assert_equal 2, topics.size - assert topics.include?(topics(:first)) - - assert_equal [], Topic.find_all_by_title("The First Topic!!") - end - - def test_find_all_by_one_attribute_which_is_a_symbol - topics = Topic.find_all_by_content("Have a nice day".to_sym) - assert_equal 2, topics.size - assert topics.include?(topics(:first)) - - assert_equal [], Topic.find_all_by_title("The First Topic!!") - end - - def test_find_all_by_one_attribute_that_is_an_aggregate - balance = customers(:david).balance - assert_kind_of Money, balance - found_customers = Customer.find_all_by_balance(balance) - assert_equal 1, found_customers.size - assert_equal customers(:david), found_customers.first - end - - def test_find_all_by_two_attributes_that_are_both_aggregates - balance = customers(:david).balance - address = customers(:david).address - assert_kind_of Money, balance - assert_kind_of Address, address - found_customers = Customer.find_all_by_balance_and_address(balance, address) - assert_equal 1, found_customers.size - assert_equal customers(:david), found_customers.first - end - - def test_find_all_by_two_attributes_with_one_being_an_aggregate - balance = customers(:david).balance - assert_kind_of Money, balance - found_customers = Customer.find_all_by_balance_and_name(balance, customers(:david).name) - assert_equal 1, found_customers.size - assert_equal customers(:david), found_customers.first - end - - def test_find_all_by_one_attribute_with_options - topics = Topic.find_all_by_content("Have a nice day", :order => "id DESC") - assert_equal topics(:first), topics.last - - topics = Topic.find_all_by_content("Have a nice day", :order => "id") - assert_equal topics(:first), topics.first - end - - def test_find_all_by_array_attribute - assert_equal 2, Topic.find_all_by_title(["The First Topic", "The Second Topic of the day"]).size - end - - def test_find_all_by_boolean_attribute - topics = Topic.find_all_by_approved(false) - assert_equal 1, topics.size - assert topics.include?(topics(:first)) - - topics = Topic.find_all_by_approved(true) - assert_equal 3, topics.size - assert topics.include?(topics(:second)) - end - - def test_find_all_by_nil_and_not_nil_attributes - topics = Topic.find_all_by_last_read_and_author_name nil, "Mary" - assert_equal 1, topics.size - assert_equal "Mary", topics[0].author_name - end - - def test_find_or_create_from_one_attribute - number_of_companies = Company.count - sig38 = Company.find_or_create_by_name("38signals") - assert_equal number_of_companies + 1, Company.count - assert_equal sig38, Company.find_or_create_by_name("38signals") - assert sig38.persisted? - end - - def test_find_or_create_from_two_attributes - number_of_topics = Topic.count - another = Topic.find_or_create_by_title_and_author_name("Another topic","John") - assert_equal number_of_topics + 1, Topic.count - assert_equal another, Topic.find_or_create_by_title_and_author_name("Another topic", "John") - assert another.persisted? - end - - def test_find_or_create_from_one_attribute_bang - number_of_companies = Company.count - assert_raises(ActiveRecord::RecordInvalid) { Company.find_or_create_by_name!("") } - assert_equal number_of_companies, Company.count - sig38 = Company.find_or_create_by_name!("38signals") - assert_equal number_of_companies + 1, Company.count - assert_equal sig38, Company.find_or_create_by_name!("38signals") - assert sig38.persisted? - end - - def test_find_or_create_from_two_attributes_bang - number_of_companies = Company.count - assert_raises(ActiveRecord::RecordInvalid) { Company.find_or_create_by_name_and_firm_id!("", 17) } - assert_equal number_of_companies, Company.count - sig38 = Company.find_or_create_by_name_and_firm_id!("38signals", 17) - assert_equal number_of_companies + 1, Company.count - assert_equal sig38, Company.find_or_create_by_name_and_firm_id!("38signals", 17) - assert sig38.persisted? - assert_equal "38signals", sig38.name - assert_equal 17, sig38.firm_id - end - - def test_find_or_create_from_two_attributes_with_one_being_an_aggregate - number_of_customers = Customer.count - created_customer = Customer.find_or_create_by_balance_and_name(Money.new(123), "Elizabeth") - assert_equal number_of_customers + 1, Customer.count - assert_equal created_customer, Customer.find_or_create_by_balance(Money.new(123), "Elizabeth") - assert created_customer.persisted? - end - - def test_find_or_create_from_one_attribute_and_hash - number_of_companies = Company.count - sig38 = Company.find_or_create_by_name({:name => "38signals", :firm_id => 17, :client_of => 23}) - assert_equal number_of_companies + 1, Company.count - assert_equal sig38, Company.find_or_create_by_name({:name => "38signals", :firm_id => 17, :client_of => 23}) - assert sig38.persisted? - assert_equal "38signals", sig38.name - assert_equal 17, sig38.firm_id - assert_equal 23, sig38.client_of - end - - def test_find_or_create_from_two_attributes_and_hash - number_of_companies = Company.count - sig38 = Company.find_or_create_by_name_and_firm_id({:name => "38signals", :firm_id => 17, :client_of => 23}) - assert_equal number_of_companies + 1, Company.count - assert_equal sig38, Company.find_or_create_by_name_and_firm_id({:name => "38signals", :firm_id => 17, :client_of => 23}) - assert sig38.persisted? - assert_equal "38signals", sig38.name - assert_equal 17, sig38.firm_id - assert_equal 23, sig38.client_of - end - - def test_find_or_create_from_one_aggregate_attribute - number_of_customers = Customer.count - created_customer = Customer.find_or_create_by_balance(Money.new(123)) - assert_equal number_of_customers + 1, Customer.count - assert_equal created_customer, Customer.find_or_create_by_balance(Money.new(123)) - assert created_customer.persisted? - end - - def test_find_or_create_from_one_aggregate_attribute_and_hash - number_of_customers = Customer.count - balance = Money.new(123) - name = "Elizabeth" - created_customer = Customer.find_or_create_by_balance({:balance => balance, :name => name}) - assert_equal number_of_customers + 1, Customer.count - assert_equal created_customer, Customer.find_or_create_by_balance({:balance => balance, :name => name}) - assert created_customer.persisted? - assert_equal balance, created_customer.balance - assert_equal name, created_customer.name - end - - def test_find_or_initialize_from_one_attribute - sig38 = Company.find_or_initialize_by_name("38signals") - assert_equal "38signals", sig38.name - assert !sig38.persisted? - end - - def test_find_or_initialize_from_one_aggregate_attribute - new_customer = Customer.find_or_initialize_by_balance(Money.new(123)) - assert_equal 123, new_customer.balance.amount - assert !new_customer.persisted? - end - - def test_find_or_initialize_from_one_attribute_should_set_attribute - c = Company.find_or_initialize_by_name_and_rating("Fortune 1000", 1000) - assert_equal "Fortune 1000", c.name - assert_equal 1000, c.rating - assert c.valid? - assert !c.persisted? - end - - def test_find_or_create_from_one_attribute_should_set_attribute - c = Company.find_or_create_by_name_and_rating("Fortune 1000", 1000) - assert_equal "Fortune 1000", c.name - assert_equal 1000, c.rating - assert c.valid? - assert c.persisted? - end - - def test_find_or_initialize_from_one_attribute_should_set_attribute_even_when_set_the_hash - c = Company.find_or_initialize_by_rating(1000, {:name => "Fortune 1000"}) - assert_equal "Fortune 1000", c.name - assert_equal 1000, c.rating - assert c.valid? - assert !c.persisted? - end - - def test_find_or_create_from_one_attribute_should_set_attribute_even_when_set_the_hash - c = Company.find_or_create_by_rating(1000, {:name => "Fortune 1000"}) - assert_equal "Fortune 1000", c.name - assert_equal 1000, c.rating - assert c.valid? - assert c.persisted? - end - - def test_find_or_initialize_should_set_attributes_if_given_as_block - c = Company.find_or_initialize_by_name(:name => "Fortune 1000") { |f| f.rating = 1000 } - assert_equal "Fortune 1000", c.name - assert_equal 1000.to_f, c.rating.to_f - assert c.valid? - assert !c.persisted? - end - - def test_find_or_create_should_set_attributes_if_given_as_block - c = Company.find_or_create_by_name(:name => "Fortune 1000") { |f| f.rating = 1000 } - assert_equal "Fortune 1000", c.name - assert_equal 1000.to_f, c.rating.to_f - assert c.valid? - assert c.persisted? - end - - def test_find_or_create_should_work_with_block_on_first_call - class << Company - undef_method(:find_or_create_by_name) if method_defined?(:find_or_create_by_name) - end - c = Company.find_or_create_by_name(:name => "Fortune 1000") { |f| f.rating = 1000 } - assert_equal "Fortune 1000", c.name - assert_equal 1000.to_f, c.rating.to_f - assert c.valid? - assert c.persisted? - end - - def test_find_or_initialize_from_two_attributes - another = Topic.find_or_initialize_by_title_and_author_name("Another topic","John") - assert_equal "Another topic", another.title - assert_equal "John", another.author_name - assert !another.persisted? - end - - def test_find_or_initialize_from_two_attributes_but_passing_only_one - assert_raise(ArgumentError) { Topic.find_or_initialize_by_title_and_author_name("Another topic") } - end - - def test_find_or_initialize_from_one_aggregate_attribute_and_one_not - new_customer = Customer.find_or_initialize_by_balance_and_name(Money.new(123), "Elizabeth") - assert_equal 123, new_customer.balance.amount - assert_equal "Elizabeth", new_customer.name - assert !new_customer.persisted? - end - - def test_find_or_initialize_from_one_attribute_and_hash - sig38 = Company.find_or_initialize_by_name({:name => "38signals", :firm_id => 17, :client_of => 23}) - assert_equal "38signals", sig38.name - assert_equal 17, sig38.firm_id - assert_equal 23, sig38.client_of - assert !sig38.persisted? - end - - def test_find_or_initialize_from_one_aggregate_attribute_and_hash - balance = Money.new(123) - name = "Elizabeth" - new_customer = Customer.find_or_initialize_by_balance({:balance => balance, :name => name}) - assert_equal balance, new_customer.balance - assert_equal name, new_customer.name - assert !new_customer.persisted? - end - - def test_find_last_by_one_attribute - assert_equal Topic.last, Topic.find_last_by_title(Topic.last.title) - assert_nil Topic.find_last_by_title("A title with no matches") - end - - def test_find_last_by_invalid_method_syntax - assert_raise(NoMethodError) { Topic.fail_to_find_last_by_title("The First Topic") } - assert_raise(NoMethodError) { Topic.find_last_by_title?("The First Topic") } - end - - def test_find_last_by_one_attribute_with_several_options - assert_equal accounts(:signals37), Account.order('id DESC').where('id != ?', 3).find_last_by_credit_limit(50) - end - - def test_find_last_by_one_missing_attribute - assert_raise(NoMethodError) { Topic.find_last_by_undertitle("The Last Topic!") } - end - - def test_find_last_by_two_attributes - topic = Topic.last - assert_equal topic, Topic.find_last_by_title_and_author_name(topic.title, topic.author_name) - assert_nil Topic.find_last_by_title_and_author_name(topic.title, "Anonymous") - end - - def test_find_last_with_limit_gives_same_result_when_loaded_and_unloaded - scope = Topic.limit(2) - unloaded_last = scope.last - loaded_last = scope.to_a.last - assert_equal loaded_last, unloaded_last - end - - def test_find_last_with_limit_and_offset_gives_same_result_when_loaded_and_unloaded - scope = Topic.offset(2).limit(2) - unloaded_last = scope.last - loaded_last = scope.to_a.last - assert_equal loaded_last, unloaded_last - end - - def test_find_last_with_offset_gives_same_result_when_loaded_and_unloaded - scope = Topic.offset(3) - unloaded_last = scope.last - loaded_last = scope.to_a.last - assert_equal loaded_last, unloaded_last - end - - def test_find_all_by_nil_attribute - topics = Topic.find_all_by_last_read nil - assert_equal 3, topics.size - assert topics.collect(&:last_read).all?(&:nil?) - end - - def test_forwarding_to_dynamic_finders - welcome = Post.find(1) - assert_equal 4, Category.find_all_by_type('SpecialCategory').size - assert_equal 0, welcome.categories.find_all_by_type('SpecialCategory').size - assert_equal 2, welcome.categories.find_all_by_type('Category').size - end - - def test_dynamic_find_all_should_respect_association_order - assert_equal [companies(:second_client), companies(:first_client)], companies(:first_firm).clients_sorted_desc.where("type = 'Client'").to_a - assert_equal [companies(:second_client), companies(:first_client)], companies(:first_firm).clients_sorted_desc.find_all_by_type('Client') - end - - def test_dynamic_find_all_should_respect_association_limit - assert_equal 1, companies(:first_firm).limited_clients.where("type = 'Client'").to_a.length - assert_equal 1, companies(:first_firm).limited_clients.find_all_by_type('Client').length - end - - def test_dynamic_find_all_limit_should_override_association_limit - assert_equal 2, companies(:first_firm).limited_clients.where("type = 'Client'").limit(9_000).to_a.length - assert_equal 2, companies(:first_firm).limited_clients.find_all_by_type('Client', :limit => 9_000).length - end - - def test_dynamic_find_last_without_specified_order - assert_equal companies(:second_client), companies(:first_firm).unsorted_clients.find_last_by_type('Client') - end - - def test_dynamic_find_or_create_from_two_attributes_using_an_association - author = authors(:david) - number_of_posts = Post.count - another = author.posts.find_or_create_by_title_and_body("Another Post", "This is the Body") - assert_equal number_of_posts + 1, Post.count - assert_equal another, author.posts.find_or_create_by_title_and_body("Another Post", "This is the Body") - assert another.persisted? - end - - def test_dynamic_find_all_should_respect_association_order_for_through - assert_equal [Comment.find(10), Comment.find(7), Comment.find(6), Comment.find(3)], authors(:david).comments_desc.where("comments.type = 'SpecialComment'").to_a - assert_equal [Comment.find(10), Comment.find(7), Comment.find(6), Comment.find(3)], authors(:david).comments_desc.find_all_by_type('SpecialComment') - end - - def test_dynamic_find_all_should_respect_association_limit_for_through - assert_equal 1, authors(:david).limited_comments.where("comments.type = 'SpecialComment'").to_a.length - assert_equal 1, authors(:david).limited_comments.find_all_by_type('SpecialComment').length - end - - def test_dynamic_find_all_order_should_override_association_limit_for_through - assert_equal 4, authors(:david).limited_comments.where("comments.type = 'SpecialComment'").limit(9_000).to_a.length - assert_equal 4, authors(:david).limited_comments.find_all_by_type('SpecialComment', :limit => 9_000).length - end - - def test_find_all_include_over_the_same_table_for_through - assert_equal 2, people(:michael).posts.includes(:people).to_a.length - end - - def test_find_or_create_by_resets_cached_counters - person = Person.create! :first_name => 'tenderlove' - post = Post.first - - assert_equal [], person.readers - assert_nil person.readers.find_by_post_id(post.id) - - person.readers.find_or_create_by_post_id(post.id) - - assert_equal 1, person.readers.count - assert_equal 1, person.readers.length - assert_equal post, person.readers.first.post - assert_equal person, person.readers.first.person - end - - def test_find_or_initialize - the_client = companies(:first_firm).clients.find_or_initialize_by_name("Yet another client") - assert_equal companies(:first_firm).id, the_client.firm_id - assert_equal "Yet another client", the_client.name - assert !the_client.persisted? - end - - def test_find_or_create_updates_size - number_of_clients = companies(:first_firm).clients.size - the_client = companies(:first_firm).clients.find_or_create_by_name("Yet another client") - assert_equal number_of_clients + 1, companies(:first_firm, :reload).clients.size - assert_equal the_client, companies(:first_firm).clients.find_or_create_by_name("Yet another client") - assert_equal number_of_clients + 1, companies(:first_firm, :reload).clients.size - end - - def test_find_or_initialize_updates_collection_size - number_of_clients = companies(:first_firm).clients_of_firm.size - companies(:first_firm).clients_of_firm.find_or_initialize_by_name("name" => "Another Client") - assert_equal number_of_clients + 1, companies(:first_firm).clients_of_firm.size - end - - def test_find_or_initialize_returns_the_instantiated_object - client = companies(:first_firm).clients_of_firm.find_or_initialize_by_name("name" => "Another Client") - assert_equal client, companies(:first_firm).clients_of_firm[-1] - end - - def test_find_or_initialize_only_instantiates_a_single_object - number_of_clients = Client.count - companies(:first_firm).clients_of_firm.find_or_initialize_by_name("name" => "Another Client").save! - companies(:first_firm).save! - assert_equal number_of_clients+1, Client.count - end - - def test_find_or_create_with_hash - post = authors(:david).posts.find_or_create_by_title(:title => 'Yet another post', :body => 'somebody') - assert_equal post, authors(:david).posts.find_or_create_by_title(:title => 'Yet another post', :body => 'somebody') - assert post.persisted? - end - - def test_find_or_create_with_one_attribute_followed_by_hash - post = authors(:david).posts.find_or_create_by_title('Yet another post', :body => 'somebody') - assert_equal post, authors(:david).posts.find_or_create_by_title('Yet another post', :body => 'somebody') - assert post.persisted? - end - - def test_find_or_create_should_work_with_block - post = authors(:david).posts.find_or_create_by_title('Yet another post') {|p| p.body = 'somebody'} - assert_equal post, authors(:david).posts.find_or_create_by_title('Yet another post') {|p| p.body = 'somebody'} - assert post.persisted? - end - - def test_forwarding_to_dynamic_finders_2 - welcome = Post.find(1) - assert_equal 4, Comment.find_all_by_type('Comment').size - assert_equal 2, welcome.comments.find_all_by_type('Comment').size - end - - def test_dynamic_find_all_by_attributes - authors = Author.all - - davids = authors.find_all_by_name('David') - assert_kind_of Array, davids - assert_equal [authors(:david)], davids - end - - def test_dynamic_find_or_initialize_by_attributes - authors = Author.all - - lifo = authors.find_or_initialize_by_name('Lifo') - assert_equal "Lifo", lifo.name - assert !lifo.persisted? - - assert_equal authors(:david), authors.find_or_initialize_by_name(:name => 'David') - end - - def test_dynamic_find_or_create_by_attributes - authors = Author.all - - lifo = authors.find_or_create_by_name('Lifo') - assert_equal "Lifo", lifo.name - assert lifo.persisted? - - assert_equal authors(:david), authors.find_or_create_by_name(:name => 'David') - end - - def test_dynamic_find_or_create_by_attributes_bang - authors = Author.all - - assert_raises(ActiveRecord::RecordInvalid) { authors.find_or_create_by_name!('') } - - lifo = authors.find_or_create_by_name!('Lifo') - assert_equal "Lifo", lifo.name - assert lifo.persisted? - - assert_equal authors(:david), authors.find_or_create_by_name!(:name => 'David') - end - - def test_finder_block - t = Topic.first - found = nil - Topic.find_by_id(t.id) { |f| found = f } - assert_equal t, found - end - - def test_finder_block_nothing_found - bad_id = Topic.maximum(:id) + 1 - assert_nil Topic.find_by_id(bad_id) { |f| raise } - end - - def test_find_returns_block_value - t = Topic.first - x = Topic.find_by_id(t.id) { |f| "hi mom!" } - assert_equal "hi mom!", x - end - - def test_dynamic_finder_with_invalid_params - assert_raise(ArgumentError) { Topic.find_by_title 'No Title', :join => "It should be `joins'" } - end - - def test_find_by_one_attribute_with_order_option - assert_equal accounts(:signals37), Account.find_by_credit_limit(50, :order => 'id') - assert_equal accounts(:rails_core_account), Account.find_by_credit_limit(50, :order => 'id DESC') - end - - def test_dynamic_find_by_attributes_should_yield_found_object - david = authors(:david) - yielded_value = nil - Author.find_by_name(david.name) do |author| - yielded_value = author - end - assert_equal david, yielded_value - end -end - -class DynamicScopeTest < ActiveRecord::TestCase - fixtures :posts - - def setup - @test_klass = Class.new(Post) do - def self.name; "Post"; end - end - @deprecation_behavior = ActiveSupport::Deprecation.behavior - ActiveSupport::Deprecation.behavior = :silence - end - - def teardown - ActiveSupport::Deprecation.behavior = @deprecation_behavior - end - - def test_dynamic_scope - assert_equal @test_klass.scoped_by_author_id(1).find(1), @test_klass.find(1) - assert_equal @test_klass.scoped_by_author_id_and_title(1, "Welcome to the weblog").first, @test_klass.all.merge!(:where => { :author_id => 1, :title => "Welcome to the weblog"}).first - end - - def test_dynamic_scope_should_create_methods_after_hitting_method_missing - assert @test_klass.methods.grep(/scoped_by_type/).blank? - @test_klass.scoped_by_type(nil) - assert @test_klass.methods.grep(/scoped_by_type/).present? - end - - def test_dynamic_scope_with_less_number_of_arguments - assert_raise(ArgumentError){ @test_klass.scoped_by_author_id_and_title(1) } - end -end - -class DynamicScopeMatchTest < ActiveRecord::TestCase - def test_scoped_by_no_match - assert_nil ActiveRecord::DynamicMatchers::Method.match(nil, "not_scoped_at_all") - end - - def test_scoped_by - model = stub(attribute_aliases: {}) - match = ActiveRecord::DynamicMatchers::Method.match(model, "scoped_by_age_and_sex_and_location") - assert_not_nil match - assert_equal %w(age sex location), match.attribute_names - end -end diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb index 36b87033ae..b277ef0317 100644 --- a/activerecord/test/cases/dirty_test.rb +++ b/activerecord/test/cases/dirty_test.rb @@ -608,20 +608,6 @@ class DirtyTest < ActiveRecord::TestCase end end - test "partial_updates config attribute is deprecated" do - klass = Class.new(ActiveRecord::Base) - - assert klass.partial_writes? - assert_deprecated { assert klass.partial_updates? } - assert_deprecated { assert klass.partial_updates } - - assert_deprecated { klass.partial_updates = false } - - assert !klass.partial_writes? - assert_deprecated { assert !klass.partial_updates? } - assert_deprecated { assert !klass.partial_updates } - end - private def with_partial_writes(klass, on = true) old = klass.partial_writes? diff --git a/activerecord/test/cases/disconnected_test.rb b/activerecord/test/cases/disconnected_test.rb index cc2c1f6489..1fecfd077e 100644 --- a/activerecord/test/cases/disconnected_test.rb +++ b/activerecord/test/cases/disconnected_test.rb @@ -7,13 +7,14 @@ class TestDisconnectedAdapter < ActiveRecord::TestCase self.use_transactional_fixtures = false def setup + skip "in-memory database mustn't disconnect" if in_memory_db? @connection = ActiveRecord::Base.connection end def teardown + return if in_memory_db? spec = ActiveRecord::Base.connection_config ActiveRecord::Base.establish_connection(spec) - @connection = nil end test "can't execute statements while disconnected" do diff --git a/activerecord/test/cases/finder_respond_to_test.rb b/activerecord/test/cases/finder_respond_to_test.rb index 9440cd429a..6101eb7b68 100644 --- a/activerecord/test/cases/finder_respond_to_test.rb +++ b/activerecord/test/cases/finder_respond_to_test.rb @@ -21,16 +21,6 @@ class FinderRespondToTest < ActiveRecord::TestCase assert_respond_to Topic, :find_by_title end - def test_should_respond_to_find_all_by_one_attribute - ensure_topic_method_is_not_cached(:find_all_by_title) - assert_respond_to Topic, :find_all_by_title - end - - def test_should_respond_to_find_all_by_two_attributes - ensure_topic_method_is_not_cached(:find_all_by_title_and_author_name) - assert_respond_to Topic, :find_all_by_title_and_author_name - end - def test_should_respond_to_find_by_two_attributes ensure_topic_method_is_not_cached(:find_by_title_and_author_name) assert_respond_to Topic, :find_by_title_and_author_name @@ -41,36 +31,6 @@ class FinderRespondToTest < ActiveRecord::TestCase assert_respond_to Topic, :find_by_heading end - def test_should_respond_to_find_or_initialize_from_one_attribute - ensure_topic_method_is_not_cached(:find_or_initialize_by_title) - assert_respond_to Topic, :find_or_initialize_by_title - end - - def test_should_respond_to_find_or_initialize_from_two_attributes - ensure_topic_method_is_not_cached(:find_or_initialize_by_title_and_author_name) - assert_respond_to Topic, :find_or_initialize_by_title_and_author_name - end - - def test_should_respond_to_find_or_create_from_one_attribute - ensure_topic_method_is_not_cached(:find_or_create_by_title) - assert_respond_to Topic, :find_or_create_by_title - end - - def test_should_respond_to_find_or_create_from_two_attributes - ensure_topic_method_is_not_cached(:find_or_create_by_title_and_author_name) - assert_respond_to Topic, :find_or_create_by_title_and_author_name - end - - def test_should_respond_to_find_or_create_from_one_attribute_bang - ensure_topic_method_is_not_cached(:find_or_create_by_title!) - assert_respond_to Topic, :find_or_create_by_title! - end - - def test_should_respond_to_find_or_create_from_two_attributes_bang - ensure_topic_method_is_not_cached(:find_or_create_by_title_and_author_name!) - assert_respond_to Topic, :find_or_create_by_title_and_author_name! - end - def test_should_not_respond_to_find_by_one_missing_attribute assert !Topic.respond_to?(:find_by_undertitle) end diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb index df6edc4057..2aa56ebc55 100644 --- a/activerecord/test/cases/fixtures_test.rb +++ b/activerecord/test/cases/fixtures_test.rb @@ -493,10 +493,6 @@ class CustomConnectionFixturesTest < ActiveRecord::TestCase fixtures :courses self.use_transactional_fixtures = false - def test_connection_instance_method_deprecation - assert_deprecated { courses(:ruby).connection } - end - def test_leaky_destroy assert_nothing_raised { courses(:ruby) } courses(:ruby).destroy diff --git a/activerecord/test/cases/invalid_connection_test.rb b/activerecord/test/cases/invalid_connection_test.rb index f6fe7f0d7d..f2d8f18ec7 100644 --- a/activerecord/test/cases/invalid_connection_test.rb +++ b/activerecord/test/cases/invalid_connection_test.rb @@ -1,20 +1,22 @@ require "cases/helper" -require "models/bird" class TestAdapterWithInvalidConnection < ActiveRecord::TestCase self.use_transactional_fixtures = false + class Bird < ActiveRecord::Base + end + def setup - @spec = ActiveRecord::Base.connection_config - non_existing_spec = {adapter: @spec[:adapter], database: "i_do_not_exist"} - ActiveRecord::Base.establish_connection(non_existing_spec) + # Can't just use current adapter; sqlite3 will create a database + # file on the fly. + Bird.establish_connection adapter: 'mysql', database: 'i_do_not_exist' end def teardown - ActiveRecord::Base.establish_connection(@spec) + Bird.remove_connection end test "inspect on Model class does not raise" do - assert_equal "Bird(no database connection)", Bird.inspect + assert_equal "#{Bird.name}(no database connection)", Bird.inspect end end diff --git a/activerecord/test/cases/locking_test.rb b/activerecord/test/cases/locking_test.rb index 0030f1b464..827fcf3d50 100644 --- a/activerecord/test/cases/locking_test.rb +++ b/activerecord/test/cases/locking_test.rb @@ -8,6 +8,7 @@ require 'models/legacy_thing' require 'models/reference' require 'models/string_key_object' require 'models/car' +require 'models/bulb' require 'models/engine' require 'models/wheel' require 'models/treasure' diff --git a/activerecord/test/cases/migration/column_attributes_test.rb b/activerecord/test/cases/migration/column_attributes_test.rb index ec2926632c..6f5f29f0da 100644 --- a/activerecord/test/cases/migration/column_attributes_test.rb +++ b/activerecord/test/cases/migration/column_attributes_test.rb @@ -168,26 +168,6 @@ module ActiveRecord assert_equal Date, bob.favorite_day.class end - # Oracle adapter stores Time or DateTime with timezone value already in _before_type_cast column - # therefore no timezone change is done afterwards when default timezone is changed - unless current_adapter?(:OracleAdapter) - # Test DateTime column and defaults, including timezone. - # FIXME: moment of truth may be Time on 64-bit platforms. - if bob.moment_of_truth.is_a?(DateTime) - - with_env_tz 'US/Eastern' do - bob.reload - assert_equal DateTime.local_offset, bob.moment_of_truth.offset - assert_not_equal 0, bob.moment_of_truth.offset - assert_not_equal "Z", bob.moment_of_truth.zone - # US/Eastern is -5 hours from GMT - assert_equal Rational(-5, 24), bob.moment_of_truth.offset - assert_match(/\A-05:00\Z/, bob.moment_of_truth.zone) - assert_equal DateTime::ITALY, bob.moment_of_truth.start - end - end - end - assert_instance_of TrueClass, bob.male? assert_kind_of BigDecimal, bob.wealth end diff --git a/activerecord/test/cases/migrator_test.rb b/activerecord/test/cases/migrator_test.rb index b5a69c4a92..3f9854200d 100644 --- a/activerecord/test/cases/migrator_test.rb +++ b/activerecord/test/cases/migrator_test.rb @@ -91,12 +91,6 @@ module ActiveRecord assert_equal 'AddExpressions', migrations[0].name end - def test_deprecated_constructor - assert_deprecated do - ActiveRecord::Migrator.new(:up, MIGRATIONS_ROOT + "/valid") - end - end - def test_relative_migrations list = Dir.chdir(MIGRATIONS_ROOT) do ActiveRecord::Migrator.migrations("valid/") diff --git a/activerecord/test/cases/pooled_connections_test.rb b/activerecord/test/cases/pooled_connections_test.rb index a8a9b06ec4..626c6aeaf8 100644 --- a/activerecord/test/cases/pooled_connections_test.rb +++ b/activerecord/test/cases/pooled_connections_test.rb @@ -16,22 +16,6 @@ class PooledConnectionsTest < ActiveRecord::TestCase @per_test_teardown.each {|td| td.call } end - def checkout_connections - ActiveRecord::Base.establish_connection(@connection.merge({:pool => 2, :checkout_timeout => 0.3})) - @connections = [] - @timed_out = 0 - - 4.times do - Thread.new do - begin - @connections << ActiveRecord::Base.connection_pool.checkout - rescue ActiveRecord::ConnectionTimeoutError - @timed_out += 1 - end - end.join - end - end - # Will deadlock due to lack of Monitor timeouts in 1.9 def checkout_checkin_connections(pool_size, threads) ActiveRecord::Base.establish_connection(@connection.merge({:pool => pool_size, :checkout_timeout => 0.5})) diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb index b5314bc9be..4fc738da94 100644 --- a/activerecord/test/cases/reflection_test.rb +++ b/activerecord/test/cases/reflection_test.rb @@ -186,14 +186,6 @@ class ReflectionTest < ActiveRecord::TestCase ActiveRecord::Base.store_full_sti_class = true end - def test_reflection_of_all_associations - # FIXME these assertions bust a lot - assert_equal 39, Firm.reflect_on_all_associations.size - assert_equal 29, Firm.reflect_on_all_associations(:has_many).size - assert_equal 10, Firm.reflect_on_all_associations(:has_one).size - assert_equal 0, Firm.reflect_on_all_associations(:belongs_to).size - end - def test_reflection_should_not_raise_error_when_compared_to_other_object assert_nothing_raised { Firm.reflections[:clients] == Object.new } end diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index e746ca2805..b205472cf5 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -368,7 +368,7 @@ class RelationTest < ActiveRecord::TestCase def test_respond_to_dynamic_finders relation = Topic.all - ["find_by_title", "find_by_title_and_author_name", "find_or_create_by_title", "find_or_initialize_by_title_and_author_name"].each do |method| + ["find_by_title", "find_by_title_and_author_name"].each do |method| assert_respond_to relation, method, "Topic.all should respond to #{method.inspect}" end end @@ -481,6 +481,14 @@ class RelationTest < ActiveRecord::TestCase assert_equal Post.find(1).last_comment, post.last_comment end + def test_to_sql_on_eager_join + expected = assert_sql { + Post.eager_load(:last_comment).order('comments.id DESC').to_a + }.first + actual = Post.eager_load(:last_comment).order('comments.id DESC').to_sql + assert_equal expected, actual + end + def test_loading_with_one_association_with_non_preload posts = Post.eager_load(:last_comment).order('comments.id DESC') post = posts.find { |p| p.id == 1 } @@ -1208,33 +1216,12 @@ class RelationTest < ActiveRecord::TestCase assert_equal "id", Post.all.primary_key end - def test_eager_loading_with_conditions_on_joins - scope = Post.includes(:comments) - - # This references the comments table, and so it should cause the comments to be eager - # loaded via a JOIN, rather than by subsequent queries. - scope = scope.joins( - Post.arel_table.create_join( - Post.arel_table, - Post.arel_table.create_on(Comment.arel_table[:id].eq(3)) - ) - ) - + def test_disable_implicit_join_references_is_deprecated assert_deprecated do - assert scope.eager_loading? + ActiveRecord::Base.disable_implicit_join_references = true end end - def test_turn_off_eager_loading_with_conditions_on_joins - original_value = ActiveRecord::Base.disable_implicit_join_references - ActiveRecord::Base.disable_implicit_join_references = true - - scope = Topic.where(author_email_address: 'my.example@gmail.com').includes(:replies) - assert_not scope.eager_loading? - ensure - ActiveRecord::Base.disable_implicit_join_references = original_value - end - def test_ordering_with_extra_spaces assert_equal authors(:david), Author.order('id DESC , name DESC').last end diff --git a/activerecord/test/cases/scoping/default_scoping_test.rb b/activerecord/test/cases/scoping/default_scoping_test.rb index 0f69443839..4bcc97ec44 100644 --- a/activerecord/test/cases/scoping/default_scoping_test.rb +++ b/activerecord/test/cases/scoping/default_scoping_test.rb @@ -56,7 +56,10 @@ class DefaultScopingTest < ActiveRecord::TestCase def test_default_scoping_with_threads 2.times do - Thread.new { assert DeveloperOrderedBySalary.all.to_sql.include?('salary DESC') }.join + Thread.new { + assert DeveloperOrderedBySalary.all.to_sql.include?('salary DESC') + DeveloperOrderedBySalary.connection.close + }.join end end @@ -153,9 +156,8 @@ class DefaultScopingTest < ActiveRecord::TestCase end def test_order_to_unscope_reordering - expected = DeveloperOrderedBySalary.all.collect { |dev| [dev.name, dev.id] } - received = DeveloperOrderedBySalary.order('salary DESC, name ASC').reverse_order.unscope(:order).collect { |dev| [dev.name, dev.id] } - assert_equal expected, received + scope = DeveloperOrderedBySalary.order('salary DESC, name ASC').reverse_order.unscope(:order) + assert !(scope.to_sql =~ /order/i) end def test_unscope_reverse_order @@ -361,9 +363,11 @@ class DefaultScopingTest < ActiveRecord::TestCase threads << Thread.new do Thread.current[:long_default_scope] = true assert_equal 1, ThreadsafeDeveloper.all.to_a.count + ThreadsafeDeveloper.connection.close end threads << Thread.new do assert_equal 1, ThreadsafeDeveloper.all.to_a.count + ThreadsafeDeveloper.connection.close end threads.each(&:join) end diff --git a/activerecord/test/cases/scoping/named_scoping_test.rb b/activerecord/test/cases/scoping/named_scoping_test.rb index afe32af1d1..72c9787b84 100644 --- a/activerecord/test/cases/scoping/named_scoping_test.rb +++ b/activerecord/test/cases/scoping/named_scoping_test.rb @@ -60,11 +60,6 @@ class NamedScopingTest < ActiveRecord::TestCase assert Topic.approved.respond_to?(:length) end - def test_respond_to_respects_include_private_parameter - assert !Topic.approved.respond_to?(:tables_in_string) - assert Topic.approved.respond_to?(:tables_in_string, true) - end - def test_scopes_with_options_limit_finds_to_those_matching_the_criteria_specified assert !Topic.all.merge!(:where => {:approved => true}).to_a.empty? @@ -440,24 +435,13 @@ class NamedScopingTest < ActiveRecord::TestCase end end - def test_eager_scopes_are_deprecated - klass = Class.new(ActiveRecord::Base) - klass.table_name = 'posts' - - assert_deprecated do - klass.scope :welcome_2, klass.where(:id => posts(:welcome).id) - end - assert_equal [posts(:welcome).title], klass.welcome_2.map(&:title) - end - - def test_eager_default_scope_relations_are_deprecated + def test_eager_default_scope_relations_are_remove klass = Class.new(ActiveRecord::Base) klass.table_name = 'posts' - assert_deprecated do + assert_raises(ArgumentError) do klass.send(:default_scope, klass.where(:id => posts(:welcome).id)) end - assert_equal [posts(:welcome).title], klass.all.map(&:title) end def test_subclass_merges_scopes_properly diff --git a/activerecord/test/cases/test_case.rb b/activerecord/test/cases/test_case.rb index f3f7054794..70b970639a 100644 --- a/activerecord/test/cases/test_case.rb +++ b/activerecord/test/cases/test_case.rb @@ -1,9 +1,95 @@ -ActiveSupport::Deprecation.silence do - require 'active_record/test_case' -end +require 'active_support/test_case' + +module ActiveRecord + # = Active Record Test Case + # + # Defines some test assertions to test against SQL queries. + class TestCase < ActiveSupport::TestCase #:nodoc: + def teardown + SQLCounter.clear_log + end + + def assert_date_from_db(expected, actual, message = nil) + # SybaseAdapter doesn't have a separate column type just for dates, + # so the time is in the string and incorrectly formatted + if current_adapter?(:SybaseAdapter) + assert_equal expected.to_s, actual.to_date.to_s, message + else + assert_equal expected.to_s, actual.to_s, message + end + end + + def assert_sql(*patterns_to_match) + SQLCounter.clear_log + yield + SQLCounter.log_all + ensure + failed_patterns = [] + patterns_to_match.each do |pattern| + failed_patterns << pattern unless SQLCounter.log_all.any?{ |sql| pattern === sql } + end + assert failed_patterns.empty?, "Query pattern(s) #{failed_patterns.map{ |p| p.inspect }.join(', ')} not found.#{SQLCounter.log.size == 0 ? '' : "\nQueries:\n#{SQLCounter.log.join("\n")}"}" + end + + def assert_queries(num = 1, options = {}) + ignore_none = options.fetch(:ignore_none) { num == :any } + SQLCounter.clear_log + x = yield + the_log = ignore_none ? SQLCounter.log_all : SQLCounter.log + if num == :any + assert_operator the_log.size, :>=, 1, "1 or more queries expected, but none were executed." + else + mesg = "#{the_log.size} instead of #{num} queries were executed.#{the_log.size == 0 ? '' : "\nQueries:\n#{the_log.join("\n")}"}" + assert_equal num, the_log.size, mesg + end + x + end + + def assert_no_queries(&block) + assert_queries(0, :ignore_none => true, &block) + end + + end + + class SQLCounter + class << self + attr_accessor :ignored_sql, :log, :log_all + def clear_log; self.log = []; self.log_all = []; end + end -ActiveRecord::TestCase.class_eval do - def sqlite3? connection - connection.class.name.split('::').last == "SQLite3Adapter" + self.clear_log + + self.ignored_sql = [/^PRAGMA/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^SAVEPOINT/, /^ROLLBACK TO SAVEPOINT/, /^RELEASE SAVEPOINT/, /^SHOW max_identifier_length/, /^BEGIN/, /^COMMIT/] + + # FIXME: this needs to be refactored so specific database can add their own + # ignored SQL, or better yet, use a different notification for the queries + # instead examining the SQL content. + oracle_ignored = [/^select .*nextval/i, /^SAVEPOINT/, /^ROLLBACK TO/, /^\s*select .* from all_triggers/im] + mysql_ignored = [/^SHOW TABLES/i, /^SHOW FULL FIELDS/] + postgresql_ignored = [/^\s*select\b.*\bfrom\b.*pg_namespace\b/im, /^\s*select\b.*\battname\b.*\bfrom\b.*\bpg_attribute\b/im, /^SHOW search_path/i] + sqlite3_ignored = [/^\s*SELECT name\b.*\bFROM sqlite_master/im] + + [oracle_ignored, mysql_ignored, postgresql_ignored, sqlite3_ignored].each do |db_ignored_sql| + ignored_sql.concat db_ignored_sql + end + + attr_reader :ignore + + def initialize(ignore = Regexp.union(self.class.ignored_sql)) + @ignore = ignore + end + + def call(name, start, finish, message_id, values) + sql = values[:sql] + + # FIXME: this seems bad. we should probably have a better way to indicate + # the query was cached + return if 'CACHE' == values[:name] + + self.class.log_all << sql + self.class.log << sql unless ignore =~ sql + end end + + ActiveSupport::Notifications.subscribe('sql.active_record', SQLCounter.new) end diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb index 6d66342fa5..c0cad52b22 100644 --- a/activerecord/test/cases/transactions_test.rb +++ b/activerecord/test/cases/transactions_test.rb @@ -410,16 +410,6 @@ class TransactionTest < ActiveRecord::TestCase assert !@second.destroyed?, 'not destroyed' end - if current_adapter?(:PostgreSQLAdapter) && defined?(PGconn::PQTRANS_IDLE) - def test_outside_transaction_works - assert assert_deprecated { Topic.connection.outside_transaction? } - Topic.connection.begin_db_transaction - assert assert_deprecated { !Topic.connection.outside_transaction? } - Topic.connection.rollback_db_transaction - assert assert_deprecated { Topic.connection.outside_transaction? } - end - end - def test_sqlite_add_column_in_transaction return true unless current_adapter?(:SQLite3Adapter) @@ -536,22 +526,20 @@ if current_adapter?(:PostgreSQLAdapter) # This will cause transactions to overlap and fail unless they are performed on # separate database connections. def test_transaction_per_thread - assert_nothing_raised do - threads = (1..3).map do - Thread.new do - Topic.transaction do - topic = Topic.find(1) - topic.approved = !topic.approved? - topic.save! - topic.approved = !topic.approved? - topic.save! - end - Topic.connection.close + threads = 3.times.map do + Thread.new do + Topic.transaction do + topic = Topic.find(1) + topic.approved = !topic.approved? + assert topic.save! + topic.approved = !topic.approved? + assert topic.save! end + Topic.connection.close end - - threads.each { |t| t.join } end + + threads.each { |t| t.join } end # Test for dirty reads among simultaneous transactions. @@ -603,14 +591,5 @@ if current_adapter?(:PostgreSQLAdapter) assert_equal original_salary, Developer.find(1).salary end - - test "#transaction_joinable= is deprecated" do - Developer.transaction do - conn = Developer.connection - assert conn.current_transaction.joinable? - assert_deprecated { conn.transaction_joinable = false } - assert !conn.current_transaction.joinable? - end - end end end diff --git a/activerecord/test/models/bulb.rb b/activerecord/test/models/bulb.rb index 0109ef4f83..4361188e21 100644 --- a/activerecord/test/models/bulb.rb +++ b/activerecord/test/models/bulb.rb @@ -37,3 +37,9 @@ class CustomBulb < Bulb self.frickinawesome = true if name == 'Dude' end end + +class FunkyBulb < Bulb + before_destroy do + raise "before_destroy was called" + end +end diff --git a/activerecord/test/models/car.rb b/activerecord/test/models/car.rb index ac42f444e1..a14a9febba 100644 --- a/activerecord/test/models/car.rb +++ b/activerecord/test/models/car.rb @@ -1,6 +1,7 @@ class Car < ActiveRecord::Base has_many :bulbs + has_many :funky_bulbs, class_name: 'FunkyBulb', dependent: :destroy has_many :foo_bulbs, -> { where(:name => 'foo') }, :class_name => "Bulb" has_many :frickinawesome_bulbs, -> { where :frickinawesome => true }, :class_name => "Bulb" diff --git a/activerecord/test/models/company.rb b/activerecord/test/models/company.rb index b988184f34..c5d4ec0833 100644 --- a/activerecord/test/models/company.rb +++ b/activerecord/test/models/company.rb @@ -35,13 +35,7 @@ module Namespaced end class Firm < Company - ActiveSupport::Deprecation.silence do - has_many :clients, -> { order "id" }, :dependent => :destroy, :counter_sql => - "SELECT COUNT(*) FROM companies WHERE firm_id = 1 " + - "AND (#{QUOTED_TYPE} = 'Client' OR #{QUOTED_TYPE} = 'SpecialClient' OR #{QUOTED_TYPE} = 'VerySpecialClient' )", - :before_remove => :log_before_remove, - :after_remove => :log_after_remove - end + has_many :clients, -> { order "id" }, :dependent => :destroy, :before_remove => :log_before_remove, :after_remove => :log_after_remove has_many :unsorted_clients, :class_name => "Client" has_many :unsorted_clients_with_symbol, :class_name => :Client has_many :clients_sorted_desc, -> { order "id DESC" }, :class_name => "Client" @@ -54,19 +48,6 @@ class Firm < Company has_many :clients_with_interpolated_conditions, ->(firm) { where "rating > #{firm.rating}" }, :class_name => "Client" has_many :clients_like_ms, -> { where("name = 'Microsoft'").order("id") }, :class_name => "Client" has_many :clients_like_ms_with_hash_conditions, -> { where(:name => 'Microsoft').order("id") }, :class_name => "Client" - ActiveSupport::Deprecation.silence do - has_many :clients_using_sql, :class_name => "Client", :finder_sql => proc { "SELECT * FROM companies WHERE client_of = #{id}" } - has_many :clients_using_counter_sql, :class_name => "Client", - :finder_sql => proc { "SELECT * FROM companies WHERE client_of = #{id} " }, - :counter_sql => proc { "SELECT COUNT(*) FROM companies WHERE client_of = #{id}" } - has_many :clients_using_zero_counter_sql, :class_name => "Client", - :finder_sql => proc { "SELECT * FROM companies WHERE client_of = #{id}" }, - :counter_sql => proc { "SELECT 0 FROM companies WHERE client_of = #{id}" } - has_many :no_clients_using_counter_sql, :class_name => "Client", - :finder_sql => 'SELECT * FROM companies WHERE client_of = 1000', - :counter_sql => 'SELECT COUNT(*) FROM companies WHERE client_of = 1000' - has_many :clients_using_finder_sql, :class_name => "Client", :finder_sql => 'SELECT * FROM companies WHERE 1=1' - end has_many :plain_clients, :class_name => 'Client' has_many :readonly_clients, -> { readonly }, :class_name => 'Client' has_many :clients_using_primary_key, :class_name => 'Client', @@ -114,13 +95,6 @@ class DependentFirm < Company has_one :company, :foreign_key => 'client_of', :dependent => :nullify end -class RestrictedFirm < Company - ActiveSupport::Deprecation.silence do - has_one :account, -> { order("id") }, :foreign_key => "firm_id", :dependent => :restrict - has_many :companies, -> { order("id") }, :foreign_key => 'client_of', :dependent => :restrict - end -end - class RestrictedWithExceptionFirm < Company has_one :account, -> { order("id") }, :foreign_key => "firm_id", :dependent => :restrict_with_exception has_many :companies, -> { order("id") }, :foreign_key => 'client_of', :dependent => :restrict_with_exception diff --git a/activerecord/test/models/company_in_module.rb b/activerecord/test/models/company_in_module.rb index 461bb0de09..38b0b6aafa 100644 --- a/activerecord/test/models/company_in_module.rb +++ b/activerecord/test/models/company_in_module.rb @@ -10,10 +10,6 @@ module MyApplication has_many :clients_sorted_desc, -> { order("id DESC") }, :class_name => "Client" has_many :clients_of_firm, -> { order "id" }, :foreign_key => "client_of", :class_name => "Client" has_many :clients_like_ms, -> { where("name = 'Microsoft'").order("id") }, :class_name => "Client" - ActiveSupport::Deprecation.silence do - has_many :clients_using_sql, :class_name => "Client", :finder_sql => 'SELECT * FROM companies WHERE client_of = #{id}' - end - has_one :account, :class_name => 'MyApplication::Billing::Account', :dependent => :destroy end diff --git a/activerecord/test/models/parrot.rb b/activerecord/test/models/parrot.rb index c4ee2bd19d..e76e83f314 100644 --- a/activerecord/test/models/parrot.rb +++ b/activerecord/test/models/parrot.rb @@ -21,3 +21,9 @@ end class DeadParrot < Parrot belongs_to :killer, :class_name => 'Pirate' end + +class FunkyParrot < Parrot + before_destroy do + raise "before_destroy was called" + end +end diff --git a/activerecord/test/models/project.rb b/activerecord/test/models/project.rb index f893754b9f..c094b726b4 100644 --- a/activerecord/test/models/project.rb +++ b/activerecord/test/models/project.rb @@ -7,19 +7,6 @@ class Project < ActiveRecord::Base has_and_belongs_to_many :developers_named_david, -> { where("name = 'David'").distinct }, :class_name => "Developer" has_and_belongs_to_many :developers_named_david_with_hash_conditions, -> { where(:name => 'David').distinct }, :class_name => "Developer" has_and_belongs_to_many :salaried_developers, -> { where "salary > 0" }, :class_name => "Developer" - - ActiveSupport::Deprecation.silence do - has_and_belongs_to_many :developers_with_finder_sql, :class_name => "Developer", :finder_sql => proc { "SELECT t.*, j.* FROM developers_projects j, developers t WHERE t.id = j.developer_id AND j.project_id = #{id} ORDER BY t.id" } - has_and_belongs_to_many :developers_with_multiline_finder_sql, :class_name => "Developer", :finder_sql => proc { - "SELECT - t.*, j.* - FROM - developers_projects j, - developers t WHERE t.id = j.developer_id AND j.project_id = #{id} ORDER BY t.id" - } - has_and_belongs_to_many :developers_by_sql, :class_name => "Developer", :delete_sql => proc { |record| "DELETE FROM developers_projects WHERE project_id = #{id} AND developer_id = #{record.id}" } - end - has_and_belongs_to_many :developers_with_callbacks, :class_name => "Developer", :before_add => Proc.new {|o, r| o.developers_log << "before_adding#{r.id || '<new>'}"}, :after_add => Proc.new {|o, r| o.developers_log << "after_adding#{r.id || '<new>'}"}, :before_remove => Proc.new {|o, r| o.developers_log << "before_removing#{r.id}"}, diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 45f71daa08..221ceb532a 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,3 +1,61 @@ +* Make `HashWithIndifferentAccess#select` always return the hash, even when + `Hash#select!` returns `nil`, to allow further chaining. + + *Marc Schütz* + +* Remove deprecated `String#encoding_aware?` core extensions (`core_ext/string/encoding`). + + *Arun Agrawal* + +* Remove deprecated `Module#local_constant_names` in favor of `Module#local_constants`. + + *Arun Agrawal* + +* Remove deprecated `DateTime.local_offset` in favor of `DateTime.civil_from_fromat`. + + *Arun Agrawal* + +* Remove deprecated `Logger` core extensions (`core_ext/logger.rb`). + + *Carlos Antonio da Silva* + +* Remove deprecated `Time#time_with_datetime_fallback`, `Time#utc_time` + and `Time#local_time` in favor of `Time#utc` and `Time#local`. + + *Vipul A M* + +* Remove deprecated `Hash#diff` with no replacement. + + If you're using it to compare hashes for the purpose of testing, please use + MiniTest's `assert_equal` instead. + + *Carlos Antonio da Silva* + +* Remove deprecated `Date#to_time_in_current_zone` in favor of `Date#in_time_zone`. + + *Vipul A M* + +* Remove deprecated `Proc#bind` with no replacement. + + *Carlos Antonio da Silva* + +* Remove deprecated `Array#uniq_by` and `Array#uniq_by!`, use native + `Array#uniq` and `Array#uniq!` instead. + + *Carlos Antonio da Silva* + +* Remove deprecated `ActiveSupport::BasicObject`, use `ActiveSupport::ProxyObject` instead. + + *Carlos Antonio da Silva* + +* Remove deprecated `BufferedLogger`. + + *Yves Senn* + +* Remove deprecated `assert_present` and `assert_blank` methods. + + *Yves Senn* + * Fix return value from `BacktraceCleaner#noise` when the cleaner is configured with multiple silencers. diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb index ffa6ffda4f..5e1fe9e556 100644 --- a/activesupport/lib/active_support.rb +++ b/activesupport/lib/active_support.rb @@ -39,7 +39,6 @@ module ActiveSupport eager_autoload do autoload :BacktraceCleaner - autoload :BasicObject autoload :ProxyObject autoload :Benchmarkable autoload :Cache diff --git a/activesupport/lib/active_support/all.rb b/activesupport/lib/active_support/all.rb index f537818300..151008bbaa 100644 --- a/activesupport/lib/active_support/all.rb +++ b/activesupport/lib/active_support/all.rb @@ -1,3 +1,4 @@ require 'active_support' +require 'active_support/deprecation' require 'active_support/time' require 'active_support/core_ext' diff --git a/activesupport/lib/active_support/basic_object.rb b/activesupport/lib/active_support/basic_object.rb deleted file mode 100644 index 91aac6db64..0000000000 --- a/activesupport/lib/active_support/basic_object.rb +++ /dev/null @@ -1,11 +0,0 @@ -require 'active_support/deprecation' -require 'active_support/proxy_object' - -module ActiveSupport - class BasicObject < ProxyObject # :nodoc: - def self.inherited(*) - ::ActiveSupport::Deprecation.warn 'ActiveSupport::BasicObject is deprecated! Use ActiveSupport::ProxyObject instead.' - super - end - end -end diff --git a/activesupport/lib/active_support/buffered_logger.rb b/activesupport/lib/active_support/buffered_logger.rb deleted file mode 100644 index 1cd0c2f790..0000000000 --- a/activesupport/lib/active_support/buffered_logger.rb +++ /dev/null @@ -1,21 +0,0 @@ -require 'active_support/deprecation' -require 'active_support/logger' - -module ActiveSupport - class BufferedLogger < Logger - - def initialize(*args) - self.class._deprecation_warning - super - end - - def self.inherited(*) - _deprecation_warning - super - end - - def self._deprecation_warning - ::ActiveSupport::Deprecation.warn 'ActiveSupport::BufferedLogger is deprecated! Use ActiveSupport::Logger instead.' - end - end -end diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb index c539048a85..308a1b2cd9 100644 --- a/activesupport/lib/active_support/cache.rb +++ b/activesupport/lib/active_support/cache.rb @@ -408,7 +408,7 @@ module ActiveSupport options = merged_options(options) instrument(:exist?, name) do entry = read_entry(namespaced_key(name, options), options) - entry && !entry.expired? + (entry && !entry.expired?) || false end end diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb index 2cffa342ef..5c738572a8 100644 --- a/activesupport/lib/active_support/callbacks.rb +++ b/activesupport/lib/active_support/callbacks.rb @@ -1,5 +1,6 @@ require 'active_support/concern' require 'active_support/descendants_tracker' +require 'active_support/core_ext/array/extract_options' require 'active_support/core_ext/class/attribute' require 'active_support/core_ext/kernel/reporting' require 'active_support/core_ext/kernel/singleton_class' @@ -542,14 +543,12 @@ module ActiveSupport @callbacks = nil @chain.delete_if { |c| callback.duplicates?(c) } end - end module ClassMethods - def normalize_callback_params(filters, block) # :nodoc: type = CALLBACK_FILTER_TYPES.include?(filters.first) ? filters.shift : :before - options = filters.last.is_a?(Hash) ? filters.pop : {} + options = filters.extract_options! filters.unshift(block) if block [type, filters, options.dup] end @@ -637,16 +636,16 @@ module ActiveSupport end # Remove all set callbacks for the given event. - def reset_callbacks(symbol) - callbacks = get_callbacks symbol + def reset_callbacks(name) + callbacks = get_callbacks name ActiveSupport::DescendantsTracker.descendants(self).each do |target| - chain = target.get_callbacks(symbol).dup + chain = target.get_callbacks(name).dup callbacks.each { |c| chain.delete(c) } - target.set_callbacks symbol, chain + target.set_callbacks name, chain end - self.set_callbacks symbol, callbacks.dup.clear + self.set_callbacks name, callbacks.dup.clear end # Define sets of events in the object lifecycle that support callbacks. @@ -658,10 +657,11 @@ module ActiveSupport # # * <tt>:terminator</tt> - Determines when a before filter will halt the # callback chain, preventing following callbacks from being called and - # the event from being triggered. This is a string to be eval'd. The - # result of the callback is available in the +result+ variable. + # the event from being triggered. This should be a lambda to be executed. + # The current object and the return result of the callback will be called + # with the lambda. # - # define_callbacks :validate, terminator: 'result == false' + # define_callbacks :validate, terminator: ->(target, result) { result == false } # # In this example, if any before validate callbacks returns +false+, # other callbacks are not executed. Defaults to +false+, meaning no value @@ -716,18 +716,18 @@ module ActiveSupport # define_callbacks :save, scope: [:name] # # would call <tt>Audit#save</tt>. - def define_callbacks(*callbacks) - config = callbacks.last.is_a?(Hash) ? callbacks.pop : {} - if config.key?(:terminator) && String === config[:terminator] + def define_callbacks(*names) + options = names.extract_options! + if options.key?(:terminator) && String === options[:terminator] ActiveSupport::Deprecation.warn "String based terminators are deprecated, please use a lambda" - value = config[:terminator] - l = class_eval "lambda { |result| #{value} }", __FILE__, __LINE__ - config[:terminator] = lambda { |target, result| target.instance_exec(result, &l) } + value = options[:terminator] + line = class_eval "lambda { |result| #{value} }", __FILE__, __LINE__ + options[:terminator] = lambda { |target, result| target.instance_exec(result, &line) } end - callbacks.each do |callback| - class_attribute "_#{callback}_callbacks" - set_callbacks callback, CallbackChain.new(callback, config) + names.each do |name| + class_attribute "_#{name}_callbacks" + set_callbacks name, CallbackChain.new(name, options) end end diff --git a/activesupport/lib/active_support/concern.rb b/activesupport/lib/active_support/concern.rb index b6ae86b583..b796d01dfd 100644 --- a/activesupport/lib/active_support/concern.rb +++ b/activesupport/lib/active_support/concern.rb @@ -105,25 +105,25 @@ module ActiveSupport end def self.extended(base) #:nodoc: - base.instance_variable_set("@_dependencies", []) + base.instance_variable_set(:@_dependencies, []) end def append_features(base) - if base.instance_variable_defined?("@_dependencies") - base.instance_variable_get("@_dependencies") << self + if base.instance_variable_defined?(:@_dependencies) + base.instance_variable_get(:@_dependencies) << self return false else return false if base < self @_dependencies.each { |dep| base.send(:include, dep) } super - base.extend const_get("ClassMethods") if const_defined?("ClassMethods") - base.class_eval(&@_included_block) if instance_variable_defined?("@_included_block") + base.extend const_get(:ClassMethods) if const_defined?(:ClassMethods) + base.class_eval(&@_included_block) if instance_variable_defined?(:@_included_block) end end def included(base = nil, &block) if base.nil? - raise MultipleIncludedBlocks if instance_variable_defined?("@_included_block") + raise MultipleIncludedBlocks if instance_variable_defined?(:@_included_block) @_included_block = block else diff --git a/activesupport/lib/active_support/core_ext.rb b/activesupport/lib/active_support/core_ext.rb index 998a59c618..199aa91020 100644 --- a/activesupport/lib/active_support/core_ext.rb +++ b/activesupport/lib/active_support/core_ext.rb @@ -1,4 +1,3 @@ Dir["#{File.dirname(__FILE__)}/core_ext/*.rb"].each do |path| - next if File.basename(path, '.rb') == 'logger' require path end diff --git a/activesupport/lib/active_support/core_ext/array.rb b/activesupport/lib/active_support/core_ext/array.rb index 79ba79192a..7d0c1e4c8d 100644 --- a/activesupport/lib/active_support/core_ext/array.rb +++ b/activesupport/lib/active_support/core_ext/array.rb @@ -1,6 +1,5 @@ require 'active_support/core_ext/array/wrap' require 'active_support/core_ext/array/access' -require 'active_support/core_ext/array/uniq_by' require 'active_support/core_ext/array/conversions' require 'active_support/core_ext/array/extract_options' require 'active_support/core_ext/array/grouping' diff --git a/activesupport/lib/active_support/core_ext/array/uniq_by.rb b/activesupport/lib/active_support/core_ext/array/uniq_by.rb deleted file mode 100644 index 23573c97de..0000000000 --- a/activesupport/lib/active_support/core_ext/array/uniq_by.rb +++ /dev/null @@ -1,19 +0,0 @@ -class Array - # *DEPRECATED*: Use <tt>Array#uniq</tt> instead. - # - # Returns a unique array based on the criteria in the block. - # - # [1, 2, 3, 4].uniq_by { |i| i.odd? } # => [1, 2] - def uniq_by(&block) - ActiveSupport::Deprecation.warn 'uniq_by is deprecated. Use Array#uniq instead' - uniq(&block) - end - - # *DEPRECATED*: Use <tt>Array#uniq!</tt> instead. - # - # Same as +uniq_by+, but modifies +self+. - def uniq_by!(&block) - ActiveSupport::Deprecation.warn 'uniq_by! is deprecated. Use Array#uniq! instead' - uniq!(&block) - end -end diff --git a/activesupport/lib/active_support/core_ext/date/zones.rb b/activesupport/lib/active_support/core_ext/date/zones.rb index b4548671bf..538ed00406 100644 --- a/activesupport/lib/active_support/core_ext/date/zones.rb +++ b/activesupport/lib/active_support/core_ext/date/zones.rb @@ -2,21 +2,6 @@ require 'date' require 'active_support/core_ext/time/zones' class Date - # *DEPRECATED*: Use +Date#in_time_zone+ instead. - # - # Converts Date to a TimeWithZone in the current zone if <tt>Time.zone</tt> or - # <tt>Time.zone_default</tt> is set, otherwise converts Date to a Time via - # Date#to_time. - def to_time_in_current_zone - ActiveSupport::Deprecation.warn 'Date#to_time_in_current_zone is deprecated. Use Date#in_time_zone instead', caller - - if ::Time.zone - ::Time.zone.local(year, month, day) - else - to_time - end - end - # Converts Date to a TimeWithZone in the current zone if Time.zone or Time.zone_default # is set, otherwise converts Date to a Time via Date#to_time # diff --git a/activesupport/lib/active_support/core_ext/date_time/calculations.rb b/activesupport/lib/active_support/core_ext/date_time/calculations.rb index 937567440b..7d4f716bb6 100644 --- a/activesupport/lib/active_support/core_ext/date_time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date_time/calculations.rb @@ -1,15 +1,7 @@ require 'date' -require 'active_support/deprecation' class DateTime class << self - # *DEPRECATED*: Use +DateTime.civil_from_format+ directly. - def local_offset - ActiveSupport::Deprecation.warn 'DateTime.local_offset is deprecated. Use DateTime.civil_from_format directly.' - - ::Time.local(2012).utc_offset.to_r / 86400 - end - # Returns <tt>Time.zone.now.to_datetime</tt> when <tt>Time.zone</tt> or # <tt>config.time_zone</tt> are set, otherwise returns # <tt>Time.now.to_datetime</tt>. diff --git a/activesupport/lib/active_support/core_ext/hash.rb b/activesupport/lib/active_support/core_ext/hash.rb index 501483498d..686f12c6da 100644 --- a/activesupport/lib/active_support/core_ext/hash.rb +++ b/activesupport/lib/active_support/core_ext/hash.rb @@ -1,6 +1,5 @@ require 'active_support/core_ext/hash/conversions' require 'active_support/core_ext/hash/deep_merge' -require 'active_support/core_ext/hash/diff' require 'active_support/core_ext/hash/except' require 'active_support/core_ext/hash/indifferent_access' require 'active_support/core_ext/hash/keys' diff --git a/activesupport/lib/active_support/core_ext/hash/diff.rb b/activesupport/lib/active_support/core_ext/hash/diff.rb deleted file mode 100644 index 4359213380..0000000000 --- a/activesupport/lib/active_support/core_ext/hash/diff.rb +++ /dev/null @@ -1,16 +0,0 @@ -require 'active_support/deprecation' - -class Hash - # Returns a hash that represents the difference between two hashes. - # - # {1 => 2}.diff(1 => 2) # => {} - # {1 => 2}.diff(1 => 3) # => {1 => 2} - # {}.diff(1 => 2) # => {1 => 2} - # {1 => 2, 3 => 4}.diff(1 => 2) # => {3 => 4} - def diff(other) - ActiveSupport::Deprecation.warn "Hash#diff is no longer used inside of Rails, and is being deprecated with no replacement. If you're using it to compare hashes for the purpose of testing, please use MiniTest's assert_equal instead." - dup. - delete_if { |k, v| other[k] == v }. - merge!(other.dup.delete_if { |k, v| has_key?(k) }) - end -end diff --git a/activesupport/lib/active_support/core_ext/logger.rb b/activesupport/lib/active_support/core_ext/logger.rb deleted file mode 100644 index 34de766331..0000000000 --- a/activesupport/lib/active_support/core_ext/logger.rb +++ /dev/null @@ -1,67 +0,0 @@ -require 'active_support/core_ext/class/attribute_accessors' -require 'active_support/deprecation' -require 'active_support/logger_silence' - -ActiveSupport::Deprecation.warn 'this file is deprecated and will be removed' - -# Adds the 'around_level' method to Logger. -class Logger #:nodoc: - def self.define_around_helper(level) - module_eval <<-end_eval, __FILE__, __LINE__ + 1 - def around_#{level}(before_message, after_message) # def around_debug(before_message, after_message, &block) - self.#{level}(before_message) # self.debug(before_message) - return_value = yield(self) # return_value = yield(self) - self.#{level}(after_message) # self.debug(after_message) - return_value # return_value - end # end - end_eval - end - [:debug, :info, :error, :fatal].each {|level| define_around_helper(level) } -end - -require 'logger' - -# Extensions to the built-in Ruby logger. -# -# If you want to use the default log formatter as defined in the Ruby core, then you -# will need to set the formatter for the logger as in: -# -# logger.formatter = Formatter.new -# -# You can then specify the datetime format, for example: -# -# logger.datetime_format = "%Y-%m-%d" -# -# Note: This logger is deprecated in favor of ActiveSupport::Logger -class Logger - include LoggerSilence - - alias :old_datetime_format= :datetime_format= - # Logging date-time format (string passed to +strftime+). Ignored if the formatter - # does not respond to datetime_format=. - def datetime_format=(format) - formatter.datetime_format = format if formatter.respond_to?(:datetime_format=) - end - - alias :old_datetime_format :datetime_format - # Get the logging datetime format. Returns nil if the formatter does not support - # datetime formatting. - def datetime_format - formatter.datetime_format if formatter.respond_to?(:datetime_format) - end - - alias :old_initialize :initialize - # Overwrite initialize to set a default formatter. - def initialize(*args) - old_initialize(*args) - self.formatter = SimpleFormatter.new - end - - # Simple formatter which only displays the message. - class SimpleFormatter < Logger::Formatter - # This method is invoked when a log event occurs - def call(severity, timestamp, progname, msg) - "#{String === msg ? msg : msg.inspect}\n" - end - end -end diff --git a/activesupport/lib/active_support/core_ext/module/introspection.rb b/activesupport/lib/active_support/core_ext/module/introspection.rb index 08e5f8a5c3..f1d26ef28f 100644 --- a/activesupport/lib/active_support/core_ext/module/introspection.rb +++ b/activesupport/lib/active_support/core_ext/module/introspection.rb @@ -59,20 +59,4 @@ class Module def local_constants #:nodoc: constants(false) end - - # *DEPRECATED*: Use +local_constants+ instead. - # - # Returns the names of the constants defined locally as strings. - # - # module M - # X = 1 - # end - # M.local_constant_names # => ["X"] - # - # This method is useful for forward compatibility, since Ruby 1.8 returns - # constant names as strings, whereas 1.9 returns them as symbols. - def local_constant_names - ActiveSupport::Deprecation.warn 'Module#local_constant_names is deprecated, use Module#local_constants instead' - local_constants.map { |c| c.to_s } - end end diff --git a/activesupport/lib/active_support/core_ext/module/method_transplanting.rb b/activesupport/lib/active_support/core_ext/module/method_transplanting.rb new file mode 100644 index 0000000000..b1097cc83b --- /dev/null +++ b/activesupport/lib/active_support/core_ext/module/method_transplanting.rb @@ -0,0 +1,11 @@ +class Module + ### + # TODO: remove this after 1.9 support is dropped + def methods_transplantable? # :nodoc: + x = Module.new { def foo; end } + Module.new { define_method :bar, x.instance_method(:foo) } + true + rescue TypeError + false + end +end diff --git a/activesupport/lib/active_support/core_ext/proc.rb b/activesupport/lib/active_support/core_ext/proc.rb deleted file mode 100644 index 166c3855a0..0000000000 --- a/activesupport/lib/active_support/core_ext/proc.rb +++ /dev/null @@ -1,17 +0,0 @@ -require "active_support/core_ext/kernel/singleton_class" -require "active_support/deprecation" - -class Proc #:nodoc: - def bind(object) - ActiveSupport::Deprecation.warn 'Proc#bind is deprecated and will be removed in future versions' - - block, time = self, Time.now - object.class_eval do - method_name = "__bind_#{time.to_i}_#{time.usec}" - define_method(method_name, &block) - method = instance_method(method_name) - remove_method(method_name) - method - end.bind(object) - end -end diff --git a/activesupport/lib/active_support/core_ext/string/encoding.rb b/activesupport/lib/active_support/core_ext/string/encoding.rb deleted file mode 100644 index a583b914db..0000000000 --- a/activesupport/lib/active_support/core_ext/string/encoding.rb +++ /dev/null @@ -1,8 +0,0 @@ -require 'active_support/deprecation' - -class String - def encoding_aware? - ActiveSupport::Deprecation.warn 'String#encoding_aware? is deprecated' - true - 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 c65f20c2d5..fa74fee78a 100644 --- a/activesupport/lib/active_support/core_ext/time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/time/calculations.rb @@ -3,7 +3,6 @@ require 'active_support/core_ext/time/conversions' require 'active_support/time_with_zone' require 'active_support/core_ext/time/zones' require 'active_support/core_ext/date_and_time/calculations' -require 'active_support/deprecation' class Time include DateAndTime::Calculations @@ -26,41 +25,6 @@ class Time end end - # *DEPRECATED*: Use +Time#utc+ or +Time#local+ instead. - # - # Returns a new Time if requested year can be accommodated by Ruby's Time class - # (i.e., if year is within either 1970..2038 or 1902..2038, depending on system architecture); - # otherwise returns a DateTime. - def time_with_datetime_fallback(utc_or_local, year, month=1, day=1, hour=0, min=0, sec=0, usec=0) - ActiveSupport::Deprecation.warn 'time_with_datetime_fallback is deprecated. Use Time#utc or Time#local instead', caller - time = ::Time.send(utc_or_local, year, month, day, hour, min, sec, usec) - - # This check is needed because Time.utc(y) returns a time object in the 2000s for 0 <= y <= 138. - if time.year == year - time - else - ::DateTime.civil_from_format(utc_or_local, year, month, day, hour, min, sec) - end - rescue - ::DateTime.civil_from_format(utc_or_local, year, month, day, hour, min, sec) - end - - # *DEPRECATED*: Use +Time#utc+ instead. - # - # Wraps class method +time_with_datetime_fallback+ with +utc_or_local+ set to <tt>:utc</tt>. - def utc_time(*args) - ActiveSupport::Deprecation.warn 'utc_time is deprecated. Use Time#utc instead', caller - time_with_datetime_fallback(:utc, *args) - end - - # *DEPRECATED*: Use +Time#local+ instead. - # - # Wraps class method +time_with_datetime_fallback+ with +utc_or_local+ set to <tt>:local</tt>. - def local_time(*args) - ActiveSupport::Deprecation.warn 'local_time is deprecated. Use Time#local instead', caller - time_with_datetime_fallback(:local, *args) - end - # Returns <tt>Time.zone.now</tt> when <tt>Time.zone</tt> or <tt>config.time_zone</tt> are set, otherwise just returns <tt>Time.now</tt>. def current ::Time.zone ? ::Time.zone.now : ::Time.now diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb index 95e03ba95c..3da99872c0 100644 --- a/activesupport/lib/active_support/hash_with_indifferent_access.rb +++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb @@ -228,7 +228,7 @@ module ActiveSupport def to_options!; self end def select(*args, &block) - dup.select!(*args, &block) + dup.tap {|hash| hash.select!(*args, &block)} end # Convert to a regular hash with string keys. diff --git a/activesupport/lib/active_support/log_subscriber/test_helper.rb b/activesupport/lib/active_support/log_subscriber/test_helper.rb index f9a98686d3..75f353f62c 100644 --- a/activesupport/lib/active_support/log_subscriber/test_helper.rb +++ b/activesupport/lib/active_support/log_subscriber/test_helper.rb @@ -1,5 +1,5 @@ require 'active_support/log_subscriber' -require 'active_support/buffered_logger' +require 'active_support/logger' require 'active_support/notifications' module ActiveSupport diff --git a/activesupport/lib/active_support/rescuable.rb b/activesupport/lib/active_support/rescuable.rb index 9a038dfbca..a7eba91ac5 100644 --- a/activesupport/lib/active_support/rescuable.rb +++ b/activesupport/lib/active_support/rescuable.rb @@ -1,6 +1,5 @@ require 'active_support/concern' require 'active_support/core_ext/class/attribute' -require 'active_support/core_ext/proc' require 'active_support/core_ext/string/inflections' require 'active_support/core_ext/array/extract_options' diff --git a/activesupport/lib/active_support/testing/assertions.rb b/activesupport/lib/active_support/testing/assertions.rb index 175f7ffe5a..76a591bc3b 100644 --- a/activesupport/lib/active_support/testing/assertions.rb +++ b/activesupport/lib/active_support/testing/assertions.rb @@ -92,36 +92,6 @@ module ActiveSupport def assert_no_difference(expression, message = nil, &block) assert_difference expression, 0, message, &block end - - # Test if an expression is blank. Passes if <tt>object.blank?</tt> - # is +true+. - # - # assert_blank [] # => true - # assert_blank [[]] # => [[]] is not blank - # - # An error message can be specified. - # - # assert_blank [], 'this should be blank' - def assert_blank(object, message=nil) - ActiveSupport::Deprecation.warn('"assert_blank" is deprecated. Please use "assert object.blank?" instead') - message ||= "#{object.inspect} is not blank" - assert object.blank?, message - end - - # Test if an expression is not blank. Passes if <tt>object.present?</tt> - # is +true+. - # - # assert_present({ data: 'x' }) # => true - # assert_present({}) # => {} is blank - # - # An error message can be specified. - # - # assert_present({ data: 'x' }, 'this should not be blank') - def assert_present(object, message=nil) - ActiveSupport::Deprecation.warn('"assert_present" is deprecated. Please use "assert object.present?" instead') - message ||= "#{object.inspect} is blank" - assert object.present?, message - end end end end diff --git a/activesupport/test/abstract_unit.rb b/activesupport/test/abstract_unit.rb index dd17cb64f4..939bd7bfa8 100644 --- a/activesupport/test/abstract_unit.rb +++ b/activesupport/test/abstract_unit.rb @@ -8,7 +8,6 @@ ensure end require 'active_support/core_ext/kernel/reporting' -require 'active_support/core_ext/string/encoding' silence_warnings do Encoding.default_internal = "UTF-8" diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb index ae6eaa4b60..d2382f0f5b 100644 --- a/activesupport/test/caching_test.rb +++ b/activesupport/test/caching_test.rb @@ -327,8 +327,8 @@ module CacheStoreBehavior def test_exist @cache.write('foo', 'bar') - assert @cache.exist?('foo') - assert !@cache.exist?('bar') + assert_equal true, @cache.exist?('foo') + assert_equal false, @cache.exist?('bar') end def test_nil_exist diff --git a/activesupport/test/core_ext/array_ext_test.rb b/activesupport/test/core_ext/array_ext_test.rb index 384064d81f..6cd0eb39b7 100644 --- a/activesupport/test/core_ext/array_ext_test.rb +++ b/activesupport/test/core_ext/array_ext_test.rb @@ -359,36 +359,6 @@ class ArrayExtractOptionsTests < ActiveSupport::TestCase end end -class ArrayUniqByTests < ActiveSupport::TestCase - def test_uniq_by - ActiveSupport::Deprecation.silence do - assert_equal [1,2], [1,2,3,4].uniq_by { |i| i.odd? } - assert_equal [1,2], [1,2,3,4].uniq_by(&:even?) - assert_equal((-5..0).to_a, (-5..5).to_a.uniq_by{ |i| i**2 }) - end - end - - def test_uniq_by! - a = [1,2,3,4] - ActiveSupport::Deprecation.silence do - a.uniq_by! { |i| i.odd? } - end - assert_equal [1,2], a - - a = [1,2,3,4] - ActiveSupport::Deprecation.silence do - a.uniq_by! { |i| i.even? } - end - assert_equal [1,2], a - - a = (-5..5).to_a - ActiveSupport::Deprecation.silence do - a.uniq_by! { |i| i**2 } - end - assert_equal((-5..0).to_a, a) - end -end - class ArrayWrapperTests < ActiveSupport::TestCase class FakeCollection def to_ary diff --git a/activesupport/test/core_ext/date_ext_test.rb b/activesupport/test/core_ext/date_ext_test.rb index aa5e4461fd..c32056f672 100644 --- a/activesupport/test/core_ext/date_ext_test.rb +++ b/activesupport/test/core_ext/date_ext_test.rb @@ -363,10 +363,3 @@ class DateExtBehaviorTest < ActiveSupport::TestCase end end -class DateExtConversionsTest < ActiveSupport::TestCase - def test_to_time_in_current_zone_is_deprecated - assert_deprecated(/to_time_in_current_zone/) do - Date.new(2012,6,7).to_time_in_current_zone - end - end -end diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb index c6d7ab618c..2d0c56bef5 100644 --- a/activesupport/test/core_ext/hash_ext_test.rb +++ b/activesupport/test/core_ext/hash_ext_test.rb @@ -487,6 +487,12 @@ class HashExtTest < ActiveSupport::TestCase assert_instance_of ActiveSupport::HashWithIndifferentAccess, hash end + def test_indifferent_select_returns_a_hash_when_unchanged + hash = ActiveSupport::HashWithIndifferentAccess.new(@strings).select {|k,v| true} + + assert_instance_of ActiveSupport::HashWithIndifferentAccess, hash + end + def test_indifferent_select_bang indifferent_strings = ActiveSupport::HashWithIndifferentAccess.new(@strings) indifferent_strings.select! {|k,v| v == 1} @@ -701,12 +707,6 @@ class HashExtTest < ActiveSupport::TestCase assert_equal expected, merged end - def test_diff - assert_deprecated do - assert_equal({ :a => 2 }, { :a => 2, :b => 5 }.diff({ :a => 1, :b => 5 })) - end - end - def test_slice original = { :a => 'x', :b => 'y', :c => 10 } expected = { :a => 'x', :b => 'y' } diff --git a/activesupport/test/core_ext/module_test.rb b/activesupport/test/core_ext/module_test.rb index f08c61ae92..ccf537e075 100644 --- a/activesupport/test/core_ext/module_test.rb +++ b/activesupport/test/core_ext/module_test.rb @@ -278,12 +278,6 @@ class ModuleTest < ActiveSupport::TestCase def test_local_constants assert_equal %w(Constant1 Constant3), Ab.local_constants.sort.map(&:to_s) end - - def test_local_constant_names - ActiveSupport::Deprecation.silence do - assert_equal %w(Constant1 Constant3), Ab.local_constant_names.sort.map(&:to_s) - end - end end module BarMethodAliaser diff --git a/activesupport/test/core_ext/proc_test.rb b/activesupport/test/core_ext/proc_test.rb deleted file mode 100644 index c4d5592196..0000000000 --- a/activesupport/test/core_ext/proc_test.rb +++ /dev/null @@ -1,14 +0,0 @@ -require 'abstract_unit' -require 'active_support/core_ext/proc' - -class ProcTests < ActiveSupport::TestCase - def test_bind_returns_method_with_changed_self - assert_deprecated do - block = Proc.new { self } - assert_equal self, block.call - bound_block = block.bind("hello") - assert_not_equal block, bound_block - assert_equal "hello", bound_block.call - end - end -end diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb index 8f0ebc13ea..12126fb9ef 100644 --- a/activesupport/test/core_ext/string_ext_test.rb +++ b/activesupport/test/core_ext/string_ext_test.rb @@ -651,12 +651,6 @@ class OutputSafetyTest < ActiveSupport::TestCase assert_equal 'foo'.to_yaml, 'foo'.html_safe.to_yaml(:foo => 1) end - test 'knows whether it is encoding aware' do - assert_deprecated do - assert 'ruby'.encoding_aware? - end - end - test "call to_param returns a normal string" do string = @string.html_safe assert string.html_safe? diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb index eefcdbb1b3..d43cf41201 100644 --- a/activesupport/test/core_ext/time_ext_test.rb +++ b/activesupport/test/core_ext/time_ext_test.rb @@ -578,58 +578,6 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase assert_equal 29, Time.days_in_month(2) end - def test_time_with_datetime_fallback - ActiveSupport::Deprecation.silence do - assert_equal Time.time_with_datetime_fallback(:utc, 2005, 2, 21, 17, 44, 30), Time.utc(2005, 2, 21, 17, 44, 30) - assert_equal Time.time_with_datetime_fallback(:local, 2005, 2, 21, 17, 44, 30), Time.local(2005, 2, 21, 17, 44, 30) - assert_equal Time.time_with_datetime_fallback(:utc, 2039, 2, 21, 17, 44, 30), DateTime.civil(2039, 2, 21, 17, 44, 30, 0) - assert_equal Time.time_with_datetime_fallback(:local, 2039, 2, 21, 17, 44, 30), DateTime.civil_from_format(:local, 2039, 2, 21, 17, 44, 30) - assert_equal Time.time_with_datetime_fallback(:utc, 1900, 2, 21, 17, 44, 30), DateTime.civil(1900, 2, 21, 17, 44, 30, 0) - assert_equal Time.time_with_datetime_fallback(:utc, 2005), Time.utc(2005) - assert_equal Time.time_with_datetime_fallback(:utc, 2039), DateTime.civil(2039, 1, 1, 0, 0, 0, 0) - assert_equal Time.time_with_datetime_fallback(:utc, 2005, 2, 21, 17, 44, 30, 1), Time.utc(2005, 2, 21, 17, 44, 30, 1) #with usec - # This won't overflow on 64bit linux - unless time_is_64bits? - assert_equal Time.time_with_datetime_fallback(:local, 1900, 2, 21, 17, 44, 30), DateTime.civil_from_format(:local, 1900, 2, 21, 17, 44, 30) - assert_equal Time.time_with_datetime_fallback(:utc, 2039, 2, 21, 17, 44, 30, 1), - DateTime.civil(2039, 2, 21, 17, 44, 30, 0, 0) - assert_equal ::Date::ITALY, Time.time_with_datetime_fallback(:utc, 2039, 2, 21, 17, 44, 30, 1).start # use Ruby's default start value - end - silence_warnings do - 0.upto(138) do |year| - [:utc, :local].each do |format| - assert_equal year, Time.time_with_datetime_fallback(format, year).year - end - end - end - end - end - - def test_utc_time - ActiveSupport::Deprecation.silence do - assert_equal Time.utc_time(2005, 2, 21, 17, 44, 30), Time.utc(2005, 2, 21, 17, 44, 30) - assert_equal Time.utc_time(2039, 2, 21, 17, 44, 30), DateTime.civil(2039, 2, 21, 17, 44, 30, 0) - assert_equal Time.utc_time(1901, 2, 21, 17, 44, 30), DateTime.civil(1901, 2, 21, 17, 44, 30, 0) - end - end - - def test_local_time - ActiveSupport::Deprecation.silence do - assert_equal Time.local_time(2005, 2, 21, 17, 44, 30), Time.local(2005, 2, 21, 17, 44, 30) - assert_equal Time.local_time(2039, 2, 21, 17, 44, 30), DateTime.civil_from_format(:local, 2039, 2, 21, 17, 44, 30) - - unless time_is_64bits? - assert_equal Time.local_time(1901, 2, 21, 17, 44, 30), DateTime.civil_from_format(:local, 1901, 2, 21, 17, 44, 30) - end - end - end - - def test_time_with_datetime_fallback_deprecations - assert_deprecated(/time_with_datetime_fallback/) { Time.time_with_datetime_fallback(:utc, 2012, 6, 7) } - assert_deprecated(/utc_time/) { Time.utc_time(2012, 6, 7) } - assert_deprecated(/local_time/) { Time.local_time(2012, 6, 7) } - end - def test_last_month_on_31st assert_equal Time.local(2004, 2, 29), Time.local(2004, 3, 31).last_month end @@ -832,9 +780,6 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ') end - def time_is_64bits? - Time.time_with_datetime_fallback(:utc, 2039, 2, 21, 17, 44, 30, 1).is_a?(Time) - end end class TimeExtMarshalingTest < ActiveSupport::TestCase diff --git a/activesupport/test/deprecation/basic_object_test.rb b/activesupport/test/deprecation/basic_object_test.rb deleted file mode 100644 index 4b5bed9eb1..0000000000 --- a/activesupport/test/deprecation/basic_object_test.rb +++ /dev/null @@ -1,12 +0,0 @@ -require 'abstract_unit' -require 'active_support/deprecation' -require 'active_support/basic_object' - - -class BasicObjectTest < ActiveSupport::TestCase - test 'BasicObject warns about deprecation when inherited from' do - warn = 'ActiveSupport::BasicObject is deprecated! Use ActiveSupport::ProxyObject instead.' - ActiveSupport::Deprecation.expects(:warn).with(warn).once - Class.new(ActiveSupport::BasicObject) - end -end
\ No newline at end of file diff --git a/activesupport/test/deprecation/buffered_logger_test.rb b/activesupport/test/deprecation/buffered_logger_test.rb deleted file mode 100644 index bf11a4732c..0000000000 --- a/activesupport/test/deprecation/buffered_logger_test.rb +++ /dev/null @@ -1,22 +0,0 @@ -require 'abstract_unit' -require 'active_support/buffered_logger' - -class BufferedLoggerTest < ActiveSupport::TestCase - - def test_can_be_subclassed - warn = 'ActiveSupport::BufferedLogger is deprecated! Use ActiveSupport::Logger instead.' - - ActiveSupport::Deprecation.expects(:warn).with(warn).once - - Class.new(ActiveSupport::BufferedLogger) - end - - def test_issues_deprecation_when_instantiated - warn = 'ActiveSupport::BufferedLogger is deprecated! Use ActiveSupport::Logger instead.' - - ActiveSupport::Deprecation.expects(:warn).with(warn).once - - ActiveSupport::BufferedLogger.new(STDOUT) - end - -end diff --git a/activesupport/test/test_test.rb b/activesupport/test/test_test.rb index 68f9ec6c00..0c8cc1f883 100644 --- a/activesupport/test/test_test.rb +++ b/activesupport/test/test_test.rb @@ -87,54 +87,6 @@ class AssertDifferenceTest < ActiveSupport::TestCase end end -class AssertBlankTest < ActiveSupport::TestCase - BLANK = [ EmptyTrue.new, nil, false, '', ' ', " \n\t \r ", [], {} ] - NOT_BLANK = [ EmptyFalse.new, Object.new, true, 0, 1, 'x', [nil], { nil => 0 } ] - - def test_assert_blank_true - BLANK.each { |value| - assert_deprecated { assert_blank value } - } - end - - def test_assert_blank_false - NOT_BLANK.each { |v| - assert_deprecated { - begin - assert_blank v - fail 'should not get to here' - rescue Exception => e - assert_match(/is not blank/, e.message) - end - } - } - end -end - -class AssertPresentTest < ActiveSupport::TestCase - BLANK = [ EmptyTrue.new, nil, false, '', ' ', " \n\t \r ", [], {} ] - NOT_BLANK = [ EmptyFalse.new, Object.new, true, 0, 1, 'x', [nil], { nil => 0 } ] - - def test_assert_present_true - NOT_BLANK.each { |v| - assert_deprecated { assert_present v } - } - end - - def test_assert_present_false - BLANK.each { |v| - assert_deprecated { - begin - assert_present v - fail 'should not get to here' - rescue Exception => e - assert_match(/is blank/, e.message) - end - } - } - end -end - class AlsoDoingNothingTest < ActiveSupport::TestCase end diff --git a/guides/CHANGELOG.md b/guides/CHANGELOG.md index 37257baeba..afa695d445 100644 --- a/guides/CHANGELOG.md +++ b/guides/CHANGELOG.md @@ -1,5 +1,5 @@ * Removed repetitive th tags. Instead of them added one th tag with a colspan attribute. - + *Sıtkı Bağdat* Please check [4-0-stable](https://github.com/rails/rails/blob/4-0-stable/guides/CHANGELOG.md) for previous changes. diff --git a/guides/assets/stylesheets/main.css b/guides/assets/stylesheets/main.css index ca319c91cf..898f9ff05b 100644 --- a/guides/assets/stylesheets/main.css +++ b/guides/assets/stylesheets/main.css @@ -129,6 +129,7 @@ body { font-family: Helvetica, Arial, Sans-Serif; text-decoration: none; vertical-align: middle; + cursor: pointer; } .red-button:active { border-top: none; diff --git a/guides/assets/stylesheets/print.css b/guides/assets/stylesheets/print.css index 628da105d4..bdc8ec948d 100644 --- a/guides/assets/stylesheets/print.css +++ b/guides/assets/stylesheets/print.css @@ -36,7 +36,7 @@ hr { } h1,h2,h3,h4,h5,h6 { font-family: "Helvetica Neue", Arial, "Lucida Grande", sans-serif; } -code { font:.9em "Courier New", Monaco, Courier, monospace; } +code { font:.9em "Courier New", Monaco, Courier, monospace; display:inline} img { float:left; margin:1.5em 1.5em 1.5em 0; } a img { border:none; } diff --git a/guides/code/getting_started/Gemfile.lock b/guides/code/getting_started/Gemfile.lock index 888a6b30e2..2d5c50ef5c 100644 --- a/guides/code/getting_started/Gemfile.lock +++ b/guides/code/getting_started/Gemfile.lock @@ -1,135 +1,107 @@ -GIT - remote: git://github.com/rails/activerecord-deprecated_finders.git - revision: 2e7b35d7948cefb2bba96438873d7f7bb1961a03 - specs: - activerecord-deprecated_finders (0.0.2) - -GIT - remote: git://github.com/rails/arel.git - revision: 38d0a222e275d917a2c1d093b24457bafb600a00 - specs: - arel (3.0.2.20120819075748) - -GIT - remote: git://github.com/rails/coffee-rails.git - revision: 052634e6d02d4800d7b021201cc8d5829775b3cd - specs: - coffee-rails (4.0.0.beta) - coffee-script (>= 2.2.0) - railties (>= 4.0.0.beta, < 5.0) - -GIT - remote: git://github.com/rails/sass-rails.git - revision: ae8138a89cac397c0df903dd533e2862902ce8f5 - specs: - sass-rails (4.0.0.beta) - railties (>= 4.0.0.beta, < 5.0) - sass (>= 3.1.10) - sprockets-rails (~> 2.0.0.rc0) - tilt (~> 1.3) - -GIT - remote: git://github.com/rails/sprockets-rails.git - revision: 09917104fdb42245fe369612a7b0e3d77e1ba763 - specs: - sprockets-rails (2.0.0.rc1) - actionpack (>= 3.0) - activesupport (>= 3.0) - sprockets (~> 2.8) - -PATH - remote: /Users/steve/src/rails +GEM + remote: https://rubygems.org/ specs: - actionmailer (4.0.0.beta) - actionpack (= 4.0.0.beta) + actionmailer (4.0.0) + actionpack (= 4.0.0) mail (~> 2.5.3) - actionpack (4.0.0.beta) - activesupport (= 4.0.0.beta) + actionpack (4.0.0) + activesupport (= 4.0.0) builder (~> 3.1.0) erubis (~> 2.7.0) - rack (~> 1.4.3) - rack-test (~> 0.6.1) - activemodel (4.0.0.beta) - activesupport (= 4.0.0.beta) + rack (~> 1.5.2) + rack-test (~> 0.6.2) + activemodel (4.0.0) + activesupport (= 4.0.0) builder (~> 3.1.0) - activerecord (4.0.0.beta) - activemodel (= 4.0.0.beta) - activerecord-deprecated_finders (= 0.0.2) - activesupport (= 4.0.0.beta) - arel (~> 3.0.2) - activesupport (4.0.0.beta) - i18n (~> 0.6) - minitest (~> 4.1) + activerecord (4.0.0) + activemodel (= 4.0.0) + activerecord-deprecated_finders (~> 1.0.2) + activesupport (= 4.0.0) + arel (~> 4.0.0) + activerecord-deprecated_finders (1.0.3) + activesupport (4.0.0) + i18n (~> 0.6, >= 0.6.4) + minitest (~> 4.2) multi_json (~> 1.3) thread_safe (~> 0.1) - tzinfo (~> 0.3.33) - rails (4.0.0.beta) - actionmailer (= 4.0.0.beta) - actionpack (= 4.0.0.beta) - activerecord (= 4.0.0.beta) - activesupport (= 4.0.0.beta) - bundler (>= 1.2.2, < 2.0) - railties (= 4.0.0.beta) - sprockets-rails (~> 2.0.0.rc1) - railties (4.0.0.beta) - actionpack (= 4.0.0.beta) - activesupport (= 4.0.0.beta) - rake (>= 0.8.7) - rdoc (~> 3.4) - thor (>= 0.15.4, < 2.0) - -GEM - remote: https://rubygems.org/ - specs: - atomic (1.0.1) + tzinfo (~> 0.3.37) + arel (4.0.0) + atomic (1.1.10) builder (3.1.4) + coffee-rails (4.0.0) + coffee-script (>= 2.2.0) + railties (>= 4.0.0.beta, < 5.0) coffee-script (2.2.0) coffee-script-source execjs - coffee-script-source (1.4.0) + coffee-script-source (1.6.3) erubis (2.7.0) execjs (1.4.0) multi_json (~> 1.0) - hike (1.2.1) - i18n (0.6.1) - jbuilder (1.3.0) + hike (1.2.3) + i18n (0.6.4) + jbuilder (1.4.2) activesupport (>= 3.0.0) - jquery-rails (2.2.0) + multi_json (>= 1.2.0) + jquery-rails (3.0.2) railties (>= 3.0, < 5.0) thor (>= 0.14, < 2.0) - json (1.7.6) - mail (2.5.3) - i18n (>= 0.4.0) + json (1.8.0) + mail (2.5.4) mime-types (~> 1.16) treetop (~> 1.4.8) - mime-types (1.19) - minitest (4.4.0) - multi_json (1.5.0) + mime-types (1.23) + minitest (4.7.5) + multi_json (1.7.7) polyglot (0.3.3) - rack (1.4.4) + rack (1.5.2) rack-test (0.6.2) rack (>= 1.0) - rake (10.0.3) - rdoc (3.12) + rails (4.0.0) + actionmailer (= 4.0.0) + actionpack (= 4.0.0) + activerecord (= 4.0.0) + activesupport (= 4.0.0) + bundler (>= 1.3.0, < 2.0) + railties (= 4.0.0) + sprockets-rails (~> 2.0.0) + railties (4.0.0) + actionpack (= 4.0.0) + activesupport (= 4.0.0) + rake (>= 0.8.7) + thor (>= 0.18.1, < 2.0) + rake (10.1.0) + rdoc (3.12.2) json (~> 1.4) - sass (3.2.5) - sprockets (2.8.2) + sass (3.2.9) + sass-rails (4.0.0) + railties (>= 4.0.0.beta, < 5.0) + sass (>= 3.1.10) + sprockets-rails (~> 2.0.0) + sdoc (0.3.20) + json (>= 1.1.3) + rdoc (~> 3.10) + sprockets (2.10.0) hike (~> 1.2) multi_json (~> 1.0) rack (~> 1.0) tilt (~> 1.1, != 1.3.0) + sprockets-rails (2.0.0) + actionpack (>= 3.0) + activesupport (>= 3.0) + sprockets (~> 2.8) sqlite3 (1.3.7) - thor (0.16.0) + thor (0.18.1) thread_safe (0.1.0) atomic - tilt (1.3.3) - treetop (1.4.12) + tilt (1.4.1) + treetop (1.4.14) polyglot polyglot (>= 0.3.1) - turbolinks (1.0.0) + turbolinks (1.2.0) coffee-rails - tzinfo (0.3.35) - uglifier (1.3.0) + tzinfo (0.3.37) + uglifier (2.1.1) execjs (>= 0.3.0) multi_json (~> 1.0, >= 1.0.2) @@ -137,14 +109,12 @@ PLATFORMS ruby DEPENDENCIES - activerecord-deprecated_finders! - arel! - coffee-rails! - jbuilder (~> 1.0.1) + coffee-rails + jbuilder (~> 1.2) jquery-rails - rails! - sass-rails! - sprockets-rails! + rails (= 4.0.0) + sass-rails + sdoc sqlite3 turbolinks uglifier (>= 1.0.3) diff --git a/guides/code/getting_started/app/controllers/comments_controller.rb b/guides/code/getting_started/app/controllers/comments_controller.rb index 0e3d2a6dde..b2d9bcdf7f 100644 --- a/guides/code/getting_started/app/controllers/comments_controller.rb +++ b/guides/code/getting_started/app/controllers/comments_controller.rb @@ -4,7 +4,7 @@ class CommentsController < ApplicationController def create @post = Post.find(params[:post_id]) - @comment = @post.comments.create(params[:comment].permit(:commenter, :body)) + @comment = @post.comments.create(comment_params) redirect_to post_path(@post) end @@ -14,4 +14,10 @@ class CommentsController < ApplicationController @comment.destroy redirect_to post_path(@post) end + + private + + def comment_params + params.require(:comment).permit(:commenter, :body) + end end diff --git a/guides/code/getting_started/app/controllers/posts_controller.rb b/guides/code/getting_started/app/controllers/posts_controller.rb index 6aa1409170..02689ad67b 100644 --- a/guides/code/getting_started/app/controllers/posts_controller.rb +++ b/guides/code/getting_started/app/controllers/posts_controller.rb @@ -17,7 +17,7 @@ class PostsController < ApplicationController def update @post = Post.find(params[:id]) - if @post.update(params[:post].permit(:title, :text)) + if @post.update(post_params) redirect_to action: :show, id: @post.id else render 'edit' @@ -29,7 +29,7 @@ class PostsController < ApplicationController end def create - @post = Post.new(params[:post].permit(:title, :text)) + @post = Post.new(post_params) if @post.save redirect_to action: :show, id: @post.id @@ -44,4 +44,10 @@ class PostsController < ApplicationController redirect_to action: :index end + + private + + def post_params + params.require(:post).permit(:title, :text) + end end diff --git a/guides/code/getting_started/app/views/welcome/index.html.erb b/guides/code/getting_started/app/views/welcome/index.html.erb index 738e12d7dc..56be8dd3cc 100644 --- a/guides/code/getting_started/app/views/welcome/index.html.erb +++ b/guides/code/getting_started/app/views/welcome/index.html.erb @@ -1,3 +1,4 @@ <h1>Hello, Rails!</h1> <%= link_to "My Blog", controller: "posts" %> +<%= link_to "New Post", new_post_path %> diff --git a/guides/code/getting_started/config/environments/development.rb b/guides/code/getting_started/config/environments/development.rb index d169e9452c..7e5692b08b 100644 --- a/guides/code/getting_started/config/environments/development.rb +++ b/guides/code/getting_started/config/environments/development.rb @@ -22,7 +22,7 @@ Blog::Application.configure do # Only use best-standards-support built into browsers. config.action_dispatch.best_standards_support = :builtin - # Raise an error on page load if there are pending migrations + # Raise an error on page load if there are pending migrations. config.active_record.migration_error = :page_load # Debug mode disables concatenation and preprocessing of assets. diff --git a/guides/source/3_2_release_notes.md b/guides/source/3_2_release_notes.md index e036860de2..dc4d942671 100644 --- a/guides/source/3_2_release_notes.md +++ b/guides/source/3_2_release_notes.md @@ -240,11 +240,11 @@ Action Pack In the example above, Posts controller will no longer automatically look up for a posts layout. If you need this functionality you could either remove `layout "application"` from `ApplicationController` or explicitly set it to `nil` in `PostsController`. -* Deprecated `ActionController::UnknownAction` in favour of `AbstractController::ActionNotFound`. +* Deprecated `ActionController::UnknownAction` in favor of `AbstractController::ActionNotFound`. -* Deprecated `ActionController::DoubleRenderError` in favour of `AbstractController::DoubleRenderError`. +* Deprecated `ActionController::DoubleRenderError` in favor of `AbstractController::DoubleRenderError`. -* Deprecated `method_missing` in favour of `action_missing` for missing actions. +* Deprecated `method_missing` in favor of `action_missing` for missing actions. * Deprecated `ActionController#rescue_action`, `ActionController#initialize_template_class` and `ActionController#assign_shortcuts`. diff --git a/guides/source/action_controller_overview.md b/guides/source/action_controller_overview.md index 6a91418e1f..f2abd833aa 100644 --- a/guides/source/action_controller_overview.md +++ b/guides/source/action_controller_overview.md @@ -160,7 +160,7 @@ NOTE: Support for parsing XML parameters has been extracted into a gem named `ac The `params` hash will always contain the `:controller` and `:action` keys, but you should use the methods `controller_name` and `action_name` instead to access these values. Any other parameters defined by the routing, such as `:id` will also be available. As an example, consider a listing of clients where the list can show either active or inactive clients. We can add a route which captures the `:status` parameter in a "pretty" URL: ```ruby -match '/clients/:status' => 'clients#index', foo: 'bar' +get '/clients/:status' => 'clients#index', foo: 'bar' ``` In this case, when a user opens the URL `/clients/active`, `params[:status]` will be set to "active". When this route is used, `params[:foo]` will also be set to "bar" just like it was passed in the query string. In the same way `params[:action]` will contain "index". @@ -346,7 +346,7 @@ Your application has a session for each user in which you can store small amount All session stores use a cookie to store a unique ID for each session (you must use a cookie, Rails will not allow you to pass the session ID in the URL as this is less secure). -For most stores, this ID is used to look up the session data on the server, e.g. in a database table. There is one exception, and that is the default and recommended session store - the CookieStore - which stores all session data in the cookie itself (the ID is still available to you if you need it). This has the advantage of being very lightweight and it requires zero setup in a new application in order to use the session. The cookie data is cryptographically signed to make it tamper-proof, but it is not encrypted, so anyone with access to it can read its contents but not edit it (Rails will not accept it if it has been edited). +For most stores, this ID is used to look up the session data on the server, e.g. in a database table. There is one exception, and that is the default and recommended session store - the CookieStore - which stores all session data in the cookie itself (the ID is still available to you if you need it). This has the advantage of being very lightweight and it requires zero setup in a new application in order to use the session. The cookie data is cryptographically signed to make it tamper-proof. And it is also encrypted so anyone with access to it can't read its contents. (Rails will not accept it if it has been edited). The CookieStore can store around 4kB of data — much less than the others — but this is usually enough. Storing large amounts of data in the session is discouraged no matter which session store your application uses. You should especially avoid storing complex objects (anything other than basic Ruby objects, the most common example being model instances) in the session, as the server might not be able to reassemble them between requests, which will result in an error. @@ -410,7 +410,7 @@ class ApplicationController < ActionController::Base # logging out removes it. def current_user @_current_user ||= session[:current_user_id] && - User.find_by_id(session[:current_user_id]) + User.find_by(id: session[:current_user_id]) end end ``` diff --git a/guides/source/action_mailer_basics.md b/guides/source/action_mailer_basics.md index d1dd231cf6..87a08e8661 100644 --- a/guides/source/action_mailer_basics.md +++ b/guides/source/action_mailer_basics.md @@ -216,6 +216,11 @@ Action Mailer makes it very easy to add attachments. attachments['filename.jpg'] = File.read('/path/to/filename.jpg') ``` + When the `mail` method will be triggered, it will send a multipart email with + an attachment, properly nested with the top level being `multipart/mixed` and + the first part being a `multipart/alternative` containing the plain text and + HTML email messages. + NOTE: Mail will automatically Base64 encode an attachment. If you want something different, encode your content and pass in the encoded content and encoding in a `Hash` to the `attachments` method. @@ -451,26 +456,6 @@ with the HTML and text versions setup as different parts. The order of the parts getting inserted is determined by the `:parts_order` inside of the `ActionMailer::Base.default` method. -### Sending Emails with Attachments - -Attachments can be added by using the `attachments` method: - -```ruby -class UserMailer < ActionMailer::Base - def welcome_email(user) - @user = user - @url = user_url(@user) - attachments['terms.pdf'] = File.read('/path/terms.pdf') - mail(to: @user.email, - subject: 'Please see the Terms and Conditions attached') - end -end -``` - -The above will send a multipart email with an attachment, properly nested with -the top level being `multipart/mixed` and the first part being a -`multipart/alternative` containing the plain text and HTML email messages. - ### Sending Emails with Dynamic Delivery Options If you wish to override the default delivery options (e.g. SMTP credentials) @@ -532,7 +517,7 @@ method. Here's an example: ```ruby class UserMailer < ActionMailer::Base def receive(email) - page = Page.find_by_address(email.to.first) + page = Page.find_by(address: email.to.first) page.emails.create( subject: email.subject, body: email.body diff --git a/guides/source/action_view_overview.md b/guides/source/action_view_overview.md index b3fc61f386..75f2989f5b 100644 --- a/guides/source/action_view_overview.md +++ b/guides/source/action_view_overview.md @@ -941,9 +941,9 @@ Creates a form and a scope around a specific model object that is used as a base ```html+erb <%= form_for @post do |f| %> <%= f.label :title, 'Title' %>: - <%= f.text_field :title %><br /> + <%= f.text_field :title %><br> <%= f.label :body, 'Body' %>: - <%= f.text_area :body %><br /> + <%= f.text_area :body %><br> <% end %> ``` @@ -1006,6 +1006,24 @@ text_field(:post, :title) # => <input type="text" id="post_title" name="post[title]" value="#{@post.title}" /> ``` +#### email_field + +Returns an input tag of the "email" type tailored for accessing a specified attribute. + +```ruby +email_field(:user, :email) +# => <input type="email" id="user_email" name="user[email]" value="#{@user.email}" /> +``` + +#### url_field + +Returns an input tag of the "url" type tailored for accessing a specified attribute. + +```ruby +url_field(:user, :url) +# => <input type="url" id="user_url" name="user[url]" value="#{@user.url}" /> +``` + ### FormOptionsHelper Provides a number of methods for turning different kinds of containers into a set of option tags. @@ -1372,6 +1390,24 @@ text_field_tag 'name' # => <input id="name" name="name" type="text" /> ``` +#### email_field_tag + +Creates a standard input field of email type. + +```ruby +email_field_tag 'email' +# => <input id="email" name="email" type="email" /> +``` + +#### url_field_tag + +Creates a standard input field of url type. + +```ruby +url_field_tag 'url' +# => <input id="url" name="url" type="url" /> +``` + #### date_field_tag Creates a standard input field of date type. @@ -1456,7 +1492,7 @@ number_to_human_size(1234567) # => 1.2 MB Formats a number as a percentage string. ```ruby -number_to_percentage(100, :precision => 0) # => 100% +number_to_percentage(100, precision: 0) # => 100% ``` #### number_to_phone diff --git a/guides/source/active_record_basics.md b/guides/source/active_record_basics.md index 1f25c6ae95..556c2544ff 100644 --- a/guides/source/active_record_basics.md +++ b/guides/source/active_record_basics.md @@ -105,7 +105,7 @@ depending on the purpose of these columns. Migrations](migrations.html) to create your tables, this column will be automatically created. -There are also some optional column names that will create additional features +There are also some optional column names that will add additional features to Active Record instances: * `created_at` - Automatically gets set to the current date and time when the @@ -253,7 +253,7 @@ user = User.first ```ruby # return the first user named David -david = User.find_by_name('David') +david = User.find_by(name: 'David') ``` ```ruby @@ -270,7 +270,7 @@ Once an Active Record object has been retrieved, its attributes can be modified and it can be saved to the database. ```ruby -user = User.find_by_name('David') +user = User.find_by(name: 'David') user.name = 'Dave' user.save ``` @@ -279,7 +279,7 @@ A shorthand for this is to use a hash mapping attribute names to the desired value, like so: ```ruby -user = User.find_by_name('David') +user = User.find_by(name: 'David') user.update(name: 'Dave') ``` @@ -297,7 +297,7 @@ Likewise, once retrieved an Active Record object can be destroyed which removes it from the database. ```ruby -user = User.find_by_name('David') +user = User.find_by(name: 'David') user.destroy ``` @@ -343,7 +343,7 @@ Migrations Rails provides a domain-specific language for managing a database schema called migrations. Migrations are stored in files which are executed against any -database that Active Record support using `rake`. Here's a migration that +database that Active Record supports using `rake`. Here's a migration that creates a table: ```ruby diff --git a/guides/source/active_record_callbacks.md b/guides/source/active_record_callbacks.md index bb42fab101..2e70e64b39 100644 --- a/guides/source/active_record_callbacks.md +++ b/guides/source/active_record_callbacks.md @@ -167,6 +167,7 @@ Additionally, the `after_find` callback is triggered by the following finder met * `all` * `first` * `find` +* `find_by` * `find_by_*` * `find_by_*!` * `find_by_sql` @@ -342,7 +343,7 @@ By using the `after_commit` callback we can account for this case. ```ruby class PictureFile < ActiveRecord::Base - after_commit :delete_picture_file_from_disk, :on => [:destroy] + after_commit :delete_picture_file_from_disk, on: [:destroy] def delete_picture_file_from_disk if File.exist?(filepath) diff --git a/guides/source/active_record_querying.md b/guides/source/active_record_querying.md index 407e779f9f..18a5342a2f 100644 --- a/guides/source/active_record_querying.md +++ b/guides/source/active_record_querying.md @@ -711,7 +711,7 @@ Post.order('id DESC').limit(20).unscope(:order, :limit) = Post.all You can additionally unscope specific where clauses. For example: ```ruby -Post.where(:id => 10).limit(1).unscope(where: :id, :limit).order('id DESC') = Post.order('id DESC') +Post.where(id: 10).limit(1).unscope(:where, :limit).order('id DESC') = Post.order('id DESC') ``` ### `only` @@ -1210,9 +1210,7 @@ class User < ActiveRecord::Base scope :active, -> { where state: 'active' } scope :inactive, -> { where state: 'inactive' } end -``` -```ruby User.active.inactive # => SELECT "users".* FROM "users" WHERE "users"."state" = 'active' AND "users"."state" = 'inactive' ``` diff --git a/guides/source/asset_pipeline.md b/guides/source/asset_pipeline.md index b68c24acfd..a6e3d3b0ac 100644 --- a/guides/source/asset_pipeline.md +++ b/guides/source/asset_pipeline.md @@ -69,12 +69,12 @@ Rails' old strategy was to append a date-based query string to every asset linke The query string strategy has several disadvantages: -1. **Not all caches will reliably cache content where the filename only differs by query parameters**<br /> +1. **Not all caches will reliably cache content where the filename only differs by query parameters**<br> [Steve Souders recommends](http://www.stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring/), "...avoiding a querystring for cacheable resources". He found that in this case 5-20% of requests will not be cached. Query strings in particular do not work at all with some CDNs for cache invalidation. -2. **The file name can change between nodes in multi-server environments.**<br /> +2. **The file name can change between nodes in multi-server environments.**<br> The default query string in Rails 2.x is based on the modification time of the files. When assets are deployed to a cluster, there is no guarantee that the timestamps will be the same, resulting in different values being used depending on which server handles the request. -3. **Too much cache invalidation**<br /> +3. **Too much cache invalidation**<br> When static assets are deployed with each new release of code, the mtime(time of last modification) of _all_ these files changes, forcing all remote clients to fetch them again, even when the content of those assets has not changed. Fingerprinting fixes these problems by avoiding query strings, and by ensuring that filenames are consistent based on their content. diff --git a/guides/source/association_basics.md b/guides/source/association_basics.md index c0e584a1c7..884aa6a9ea 100644 --- a/guides/source/association_basics.md +++ b/guides/source/association_basics.md @@ -2171,7 +2171,7 @@ You're not limited to the functionality that Rails automatically builds into ass class Customer < ActiveRecord::Base has_many :orders do def find_by_order_prefix(order_number) - find_by_region_id(order_number[0..2]) + find_by(region_id: order_number[0..2]) end end end diff --git a/guides/source/contributing_to_ruby_on_rails.md b/guides/source/contributing_to_ruby_on_rails.md index b9f42fa51b..24e16ecd40 100644 --- a/guides/source/contributing_to_ruby_on_rails.md +++ b/guides/source/contributing_to_ruby_on_rails.md @@ -234,8 +234,8 @@ workflow with the [rails-dev-box](https://github.com/rails/rails-dev-box). As a compromise, test what your code obviously affects, and if the change is not in railties run the whole test suite of the affected component. If all is -green that's enough to propose your contribution. We have [Travis CI](https -://travis-ci.org/) as a safety net for catching unexpected breakages +green that's enough to propose your contribution. We have [Travis CI](https://travis-ci.org/rails/rails) +as a safety net for catching unexpected breakages elsewhere. TIP: Changes that are cosmetic in nature and do not add anything substantial to the stability, functionality, or testability of Rails will generally not be accepted. diff --git a/guides/source/debugging_rails_applications.md b/guides/source/debugging_rails_applications.md index 0860e3119d..98f91c1ac6 100644 --- a/guides/source/debugging_rails_applications.md +++ b/guides/source/debugging_rails_applications.md @@ -648,7 +648,7 @@ In this section, you will learn how to find and fix such leaks by using tool suc [Valgrind](http://valgrind.org/) is a Linux-only application for detecting C-based memory leaks and race conditions. -There are Valgrind tools that can automatically detect many memory management and threading bugs, and profile your programs in detail. For example, a C extension in the interpreter calls `malloc()` but is doesn't properly call `free()`, this memory won't be available until the app terminates. +There are Valgrind tools that can automatically detect many memory management and threading bugs, and profile your programs in detail. For example, if a C extension in the interpreter calls `malloc()` but doesn't properly call `free()`, this memory won't be available until the app terminates. For further information on how to install Valgrind and use with Ruby, refer to [Valgrind and Ruby](http://blog.evanweaver.com/articles/2008/02/05/valgrind-and-ruby/) by Evan Weaver. @@ -661,6 +661,8 @@ There are some Rails plugins to help you to find errors and debug your applicati * [Query Trace](https://github.com/ntalbott/query_trace/tree/master) Adds query origin tracing to your logs. * [Query Reviewer](https://github.com/nesquena/query_reviewer) This rails plugin not only runs "EXPLAIN" before each of your select queries in development, but provides a small DIV in the rendered output of each page with the summary of warnings for each query that it analyzed. * [Exception Notifier](https://github.com/smartinez87/exception_notification/tree/master) Provides a mailer object and a default set of templates for sending email notifications when errors occur in a Rails application. +* [Better Errors](https://github.com/charliesome/better_errors) Replaces the standard Rails error page with a new one containing more contextual information, like source code and variable inspection. +* [RailsPanel](https://github.com/dejan/rails_panel) Chrome extension for Rails development that will end your tailing of development.log. Have all information about your Rails app requests in the browser - in the Developer Tools panel. Provides insight to db/rendering/total times, parameter list, rendered views and more. References ---------- diff --git a/guides/source/engines.md b/guides/source/engines.md index bc66ed256e..d714f84731 100644 --- a/guides/source/engines.md +++ b/guides/source/engines.md @@ -346,7 +346,7 @@ Next, the partial that this line will render needs to exist. Create a new direct <h3>New comment</h3> <%= form_for [@post, @post.comments.build] do |f| %> <p> - <%= f.label :text %><br /> + <%= f.label :text %><br> <%= f.text_area :text %> </p> <%= f.submit %> @@ -515,7 +515,7 @@ First, the `author_name` text field needs to be added to the `app/views/blorgh/p ```html+erb <div class="field"> - <%= f.label :author_name %><br /> + <%= f.label :author_name %><br> <%= f.text_field :author_name %> </div> ``` diff --git a/guides/source/form_helpers.md b/guides/source/form_helpers.md index 7f37a298b1..11e8db9e88 100644 --- a/guides/source/form_helpers.md +++ b/guides/source/form_helpers.md @@ -553,7 +553,7 @@ outputs (with actual option values omitted for brevity) which results in a `params` hash like ```ruby -{:person => {'birth_date(1i)' => '2008', 'birth_date(2i)' => '11', 'birth_date(3i)' => '22'}} +{'person' => {'birth_date(1i)' => '2008', 'birth_date(2i)' => '11', 'birth_date(3i)' => '22'}} ``` When this is passed to `Person.new` (or `update`), Active Record spots that these parameters should all be used to construct the `birth_date` attribute and uses the suffixed information to determine in which order it should pass these parameters to functions such as `Date.civil`. @@ -881,19 +881,19 @@ end ```ruby { - :person => { - :name => 'John Doe', - :addresses_attributes => { - '0' => { - :kind => 'Home', - :street => '221b Baker Street', - }, - '1' => { - :kind => 'Office', - :street => '31 Spooner Street' - } - } + 'person' => { + 'name' => 'John Doe', + 'addresses_attributes' => { + '0' => { + 'kind' => 'Home', + 'street' => '221b Baker Street' + }, + '1' => { + 'kind' => 'Office', + 'street' => '31 Spooner Street' + } } + } } ``` diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md index 455907535b..9b2fa315a1 100644 --- a/guides/source/getting_started.md +++ b/guides/source/getting_started.md @@ -138,7 +138,7 @@ application. Most of the work in this tutorial will happen in the `app/` folder, |config/|Configure your application's runtime rules, routes, database, and more. This is covered in more detail in [Configuring Rails Applications](configuring.html)| |config.ru|Rack configuration for Rack based servers used to start the application.| |db/|Contains your current database schema, as well as the database migrations.| -|Gemfile<br />Gemfile.lock|These files allow you to specify what gem dependencies are needed for your Rails application. These files are used by the Bundler gem. For more information about Bundler, see [the Bundler website](http://gembundler.com) | +|Gemfile<br>Gemfile.lock|These files allow you to specify what gem dependencies are needed for your Rails application. These files are used by the Bundler gem. For more information about Bundler, see [the Bundler website](http://gembundler.com) | |lib/|Extended modules for your application.| |log/|Application log files.| |public/|The only folder seen to the world as-is. Contains the static files and compiled assets.| @@ -230,16 +230,16 @@ Blog::Application.routes.draw do # first created -> highest priority. # ... # You can have the root of your site routed with "root" - # root to: "welcome#index" + # root "welcome#index" ``` -This is your application's _routing file_ which holds entries in a special DSL (domain-specific language) that tells Rails how to connect incoming requests to controllers and actions. This file contains many sample routes on commented lines, and one of them actually shows you how to connect the root of your site to a specific controller and action. Find the line beginning with `root :to` and uncomment it. It should look something like the following: +This is your application's _routing file_ which holds entries in a special DSL (domain-specific language) that tells Rails how to connect incoming requests to controllers and actions. This file contains many sample routes on commented lines, and one of them actually shows you how to connect the root of your site to a specific controller and action. Find the line beginning with `root` and uncomment it. It should look something like the following: ```ruby -root to: "welcome#index" +root "welcome#index" ``` -The `root to: "welcome#index"` tells Rails to map requests to the root of the application to the welcome controller's index action and `get "welcome/index"` tells Rails to map requests to <http://localhost:3000/welcome/index> to the welcome controller's index action. This was created earlier when you ran the controller generator (`rails generate controller welcome index`). +The `root "welcome#index"` tells Rails to map requests to the root of the application to the welcome controller's index action and `get "welcome/index"` tells Rails to map requests to <http://localhost:3000/welcome/index> to the welcome controller's index action. This was created earlier when you ran the controller generator (`rails generate controller welcome index`). If you navigate to <http://localhost:3000> in your browser, you'll see the `Hello, Rails!` message you put into `app/views/welcome/index.html.erb`, indicating that this new route is indeed going to `WelcomeController`'s `index` action and is rendering the view correctly. @@ -252,23 +252,26 @@ Now that you've seen how to create a controller, an action and a view, let's cre In the Blog application, you will now create a new _resource_. A resource is the term used for a collection of similar objects, such as posts, people or animals. You can create, read, update and destroy items for a resource and these operations are referred to as _CRUD_ operations. -Rails provides a `resources` method which can be used to declare a -standard REST resource. Here's how `config/routes.rb` will look like. +Rails provides a `resources` method which can be used to declare a standard REST resource. +Here's what `config/routes.rb` should look like after the _post resource_ is declared. ```ruby Blog::Application.routes.draw do resources :posts - root to: "welcome#index" + root "welcome#index" end ``` -If you run `rake routes`, you'll see that all the routes for the -standard RESTful actions. +If you run `rake routes`, you'll see that it has defined routes for all the +standard RESTful actions. The meaning of the prefix column (and other columns) +will be seen later, but for now notice that Rails has inferred the +singular form `post` and makes meaningful use of the distinction. ```bash $ rake routes + Prefix Verb URI Pattern Controller#Action posts GET /posts(.:format) posts#index POST /posts(.:format) posts#create new_post GET /posts/new(.:format) posts#new @@ -394,9 +397,27 @@ Edit the `form_for` line inside `app/views/posts/new.html.erb` to look like this <%= form_for :post, url: posts_path do |f| %> ``` -In this example, the `posts_path` helper is passed to the `:url` option. What Rails will do with this is that it will point the form to the `create` action of the current controller, the `PostsController`, and will send a `POST` request to that route. - -By using the `post` method rather than the `get` method, Rails will define a route that will only respond to POST methods. The POST method is the typical method used by forms all over the web. +In this example, the `posts_path` helper is passed to the `:url` option. +To see what Rails will do with this, we look back at the output of +`rake routes`: +```bash +$ rake routes + Prefix Verb URI Pattern Controller#Action + posts GET /posts(.:format) posts#index + POST /posts(.:format) posts#create + new_post GET /posts/new(.:format) posts#new +edit_post GET /posts/:id/edit(.:format) posts#edit + post GET /posts/:id(.:format) posts#show + PATCH /posts/:id(.:format) posts#update + PUT /posts/:id(.:format) posts#update + DELETE /posts/:id(.:format) posts#destroy + root / welcome#index +``` +The `posts_path` helper tells Rails to point the form +to the URI Pattern associated with the `posts` prefix; and +the form will (by default) send a `POST` request +to that route. This is associated with the +`create` action of the current controller, the `PostsController`. With the form and its associated route defined, you will be able to fill in the form and then click the submit button to begin the process of creating a new post, so go ahead and do that. When you submit the form, you should see a familiar error: @@ -531,27 +552,19 @@ and change the `create` action to look like this: ```ruby def create - @post = Post.new(post_params) + @post = Post.new(params[:post]) @post.save redirect_to @post end - -private - def post_params - params.require(:post).permit(:title, :text) - end ``` Here's what's going on: every Rails model can be initialized with its respective attributes, which are automatically mapped to the respective -database columns. In the first line we do just that (remember that -`post_params` contains the attributes we're interested in). Then, -`@post.save` is responsible for saving the model in the database. -Finally, we redirect the user to the `show` action, -which we'll define later. - -TIP: Note that `def post_params` is private. This new approach prevents an attacker from setting the model's attributes by manipulating the hash passed to the model. For more information, refer to [this blog post about Strong Parameters](http://weblog.rubyonrails.org/2012/3/21/strong-parameters/). +database columns. In the first line we do just that +(remember that `params[:post]` contains the attributes we're interested in). +Then, `@post.save` is responsible for saving the model in the database. +Finally, we redirect the user to the `show` action, which we'll define later. TIP: As we'll see later, `@post.save` returns a boolean indicating whether the model was saved or not. @@ -612,11 +625,16 @@ we want to accept in our controllers. In this case, we want to allow the look like this: ``` - def create - @post = Post.new(params[:post].permit(:title, :text)) +def create + @post = Post.new(post_params) - @post.save - redirect_to @post + @post.save + redirect_to @post +end + +private + def post_params + params.require(:post).permit(:title, :text) end ``` @@ -626,6 +644,11 @@ Visit <http://localhost:3000/posts/new> and give it a try!  +TIP: Note that `def post_params` is private. This new approach prevents an attacker from +setting the model's attributes by manipulating the hash passed to the model. +For more information, refer to +[this blog post about Strong Parameters](http://weblog.rubyonrails.org/2012/3/21/strong-parameters/). + ### Listing all posts We still need a way to list all our posts, so let's do that. @@ -721,7 +744,7 @@ TIP: In development mode (which is what you're working in by default), Rails reloads your application with every browser request, so there's no need to stop and restart the web server when a change is made. -### Allowing the update of fields +### Adding Some Validation The model file, `app/models/post.rb` is about as simple as it can get: @@ -736,8 +759,6 @@ your Rails models for free, including basic database CRUD (Create, Read, Update, Destroy) operations, data validation, as well as sophisticated search support and the ability to relate multiple models to one another. -### Adding Some Validation - Rails includes methods to help you validate the data that you send to models. Open the `app/models/post.rb` file and edit it: @@ -767,7 +788,7 @@ def new end def create - @post = Post.new(params[:post].permit(:title, :text)) + @post = Post.new(post_params) if @post.save redirect_to @post @@ -775,6 +796,11 @@ def create render 'new' end end + +private + def post_params + params.require(:post).permit(:title, :text) + end ``` The `new` action is now creating a new instance variable called `@post`, and @@ -905,12 +931,17 @@ Next we need to create the `update` action in `app/controllers/posts_controller. def update @post = Post.find(params[:id]) - if @post.update(params[:post].permit(:title, :text)) + if @post.update(post_params) redirect_to @post else render 'edit' end end + +private + def post_params + params.require(:post).permit(:title, :text) + end ``` The new method, `update`, is used when you want to update a record @@ -918,6 +949,8 @@ that already exists, and it accepts a hash containing the attributes that you want to update. As before, if there was an error updating the post we want to show the form back to the user. +We reuse the `post_params` method that we defined earlier for the create action. + TIP: You don't need to pass all attributes to `update`. For example, if you'd call `@post.update(title: 'A new title')` Rails would only update the `title` attribute, leaving all other @@ -1004,9 +1037,14 @@ content: ``` Everything except for the `form_for` declaration remained the same. -How `form_for` can figure out the right `action` and `method` attributes -when building the form will be explained in just a moment. For now, let's update the -`app/views/posts/new.html.erb` view to use this new partial, rewriting it +The reason we can use this shorter, simpler `form_for` declaration +to stand in for either of the other forms is that `@post` is a *resource* +corresponding to a full set of RESTful routes, and Rails is able to infer +which URI and method to use. +For more information about this use of `form_for`, see +[Resource-oriented style](//api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_for-label-Resource-oriented+style). + +Now, let's update the `app/views/posts/new.html.erb` view to use this new partial, rewriting it completely: ```html+erb @@ -1062,7 +1100,7 @@ You can call `destroy` on Active Record objects when you want to delete them from the database. Note that we don't need to add a view for this action since we're redirecting to the `index` action. -Finally, add a 'destroy' link to your `index` action template +Finally, add a 'Destroy' link to your `index` action template (`app/views/posts/index.html.erb`) to wrap everything together. @@ -1088,8 +1126,8 @@ together. </table> ``` -Here we're using `link_to` in a different way. We pass the named route as the first argument, -and then the final two keys as another argument. The `:method` and `:'data-confirm'` +Here we're using `link_to` in a different way. We pass the named route as the second argument, +and then the options as another argument. The `:method` and `:'data-confirm'` options are used as HTML5 attributes so that when the link is clicked, Rails will first show a confirm dialog to the user, and then submit the link with method `delete`. This is done via the JavaScript file `jquery_ujs` which is automatically included @@ -1277,11 +1315,11 @@ So first, we'll wire up the Post show template <h2>Add a comment:</h2> <%= form_for([@post, @post.comments.build]) do |f| %> <p> - <%= f.label :commenter %><br /> + <%= f.label :commenter %><br> <%= f.text_field :commenter %> </p> <p> - <%= f.label :body %><br /> + <%= f.label :body %><br> <%= f.text_area :body %> </p> <p> @@ -1303,9 +1341,14 @@ Let's wire up the `create` in `app/controllers/comments_controller.rb`: class CommentsController < ApplicationController def create @post = Post.find(params[:post_id]) - @comment = @post.comments.create(params[:comment].permit(:commenter, :body)) + @comment = @post.comments.create(comment_params) redirect_to post_path(@post) end + + private + def comment_params + params.require(:comment).permit(:commenter, :body) + end end ``` @@ -1352,11 +1395,11 @@ template. This is where we want the comment to show, so let's add that to the <h2>Add a comment:</h2> <%= form_for([@post, @post.comments.build]) do |f| %> <p> - <%= f.label :commenter %><br /> + <%= f.label :commenter %><br> <%= f.text_field :commenter %> </p> <p> - <%= f.label :body %><br /> + <%= f.label :body %><br> <%= f.text_area :body %> </p> <p> @@ -1418,11 +1461,11 @@ following: <h2>Add a comment:</h2> <%= form_for([@post, @post.comments.build]) do |f| %> <p> - <%= f.label :commenter %><br /> + <%= f.label :commenter %><br> <%= f.text_field :commenter %> </p> <p> - <%= f.label :body %><br /> + <%= f.label :body %><br> <%= f.text_area :body %> </p> <p> @@ -1448,11 +1491,11 @@ create a file `app/views/comments/_form.html.erb` containing: ```html+erb <%= form_for([@post, @post.comments.build]) do |f| %> <p> - <%= f.label :commenter %><br /> + <%= f.label :commenter %><br> <%= f.text_field :commenter %> </p> <p> - <%= f.label :body %><br /> + <%= f.label :body %><br> <%= f.text_area :body %> </p> <p> @@ -1527,10 +1570,9 @@ controller (`app/controllers/comments_controller.rb`): ```ruby class CommentsController < ApplicationController - def create @post = Post.find(params[:post_id]) - @comment = @post.comments.create(params[:comment]) + @comment = @post.comments.create(comment_params) redirect_to post_path(@post) end @@ -1541,6 +1583,10 @@ class CommentsController < ApplicationController redirect_to post_path(@post) end + private + def comment_params + params.require(:comment).permit(:commenter, :body) + end end ``` diff --git a/guides/source/i18n.md b/guides/source/i18n.md index 062da3877b..2b116c337a 100644 --- a/guides/source/i18n.md +++ b/guides/source/i18n.md @@ -21,6 +21,11 @@ This guide will walk you through the I18n API and contains a tutorial on how to After reading this guide, you will know: +* How I18n works in Ruby on Rails +* How to correctly use I18n into a RESTful application in various ways +* How to use I18n to translate ActiveRecord errors or ActionMailer E-mail subjects +* Some other tools to go further with the translation process of your application + -------------------------------------------------------------------------------- NOTE: The Ruby I18n framework provides you with all necessary means for internationalization/localization of your Rails application. You may, however, use any of various plugins and extensions available, which add additional functionality or features. See the Rails [I18n Wiki](http://rails-i18n.org/wiki) for more information. @@ -253,7 +258,7 @@ You would probably need to map URLs like these: ```ruby # config/routes.rb -match '/:locale' => 'dashboard#index' +get '/:locale' => 'dashboard#index' ``` Do take special care about the **order of your routes**, so this route declaration does not "eat" other ones. (You may want to add it directly before the `root :to` declaration.) diff --git a/guides/source/index.html.erb b/guides/source/index.html.erb index a8e4525c67..57c224c165 100644 --- a/guides/source/index.html.erb +++ b/guides/source/index.html.erb @@ -19,7 +19,7 @@ Ruby on Rails Guides <h3><%= section['name'] %></h3> <dl> <% section['documents'].each do |document| %> - <%= guide(document['name'], document['url'], :work_in_progress => document['work_in_progress']) do %> + <%= guide(document['name'], document['url'], work_in_progress: document['work_in_progress']) do %> <p><%= document['description'] %></p> <% end %> <% end %> diff --git a/guides/source/initialization.md b/guides/source/initialization.md index 738591659d..11c736585f 100644 --- a/guides/source/initialization.md +++ b/guides/source/initialization.md @@ -7,14 +7,17 @@ as of Rails 4. It is an extremely in-depth guide and recommended for advanced Ra After reading this guide, you will know: * How to use `rails server`. +* The timeline of Rails' initialization sequence. +* Where different files are required by the boot sequence. +* How the Rails::Server interface is defined and used. -------------------------------------------------------------------------------- This guide goes through every method call that is required to boot up the Ruby on Rails stack for a default Rails 4 application, explaining each part in detail along the way. For this -guide, we will be focusing on what happens when you execute +rails -server+ to boot your app. +guide, we will be focusing on what happens when you execute `rails server` +to boot your app. NOTE: Paths in this guide are relative to Rails or a Rails application unless otherwise specified. @@ -26,7 +29,7 @@ quickly. Launch! ------- -Now we finally boot and initialize the app. It all starts with your app's +Let's start to boot and initialize the app. It all begins with your app's `bin/rails` executable. A Rails application is usually started by running `rails console` or `rails server`. @@ -57,7 +60,8 @@ require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE']) In a standard Rails application, there's a `Gemfile` which declares all dependencies of the application. `config/boot.rb` sets `ENV['BUNDLE_GEMFILE']` to the location of this file. If the Gemfile -exists, `bundler/setup` is then required. +exists, then `bundler/setup` is required. The require is used by Bundler to +configure the load path for your Gemfile's dependencies. A standard Rails application depends on several gems, specifically: @@ -251,9 +255,9 @@ set earlier) is required. ### `config/application` -When `require APP_PATH` is executed, `config/application.rb` is loaded. -This file exists in your app and it's free for you to change based -on your needs. +When `require APP_PATH` is executed, `config/application.rb` is loaded (recall +that `APP_PATH` is defined in `bin/rails`). This file exists in your application +and it's free for you to change based on your needs. ### `Rails::Server#start` @@ -443,7 +447,9 @@ I18n and Rails configuration are all being defined here. ### Back to `config/environment.rb` -When `config/application.rb` has finished loading Rails, and defined +The rest of `config/application.rb` defines the configuration for the +`Rails::Application` which will be used once the application is fully +initialized. When `config/application.rb` has finished loading Rails and defined the application namespace, we go back to `config/environment.rb`, where the application is initialized. For example, if the application was called `Blog`, here we would find `Blog::Application.initialize!`, which is @@ -471,6 +477,13 @@ traverses all the class ancestors looking for an `initializers` method, sorting them and running them. For example, the `Engine` class will make all the engines available by providing the `initializers` method. +The `Rails::Application` class, as defined in `railties/lib/rails/application.rb` +defines `bootstrap`, `railtie`, and `finisher` initializers. The `bootstrap` initializers +prepare the application (like initializing the logger) while the `finisher` +initializers (like building the middleware stack) are run last. The `railtie` +initializers are the initializers which have been defined on the `Rails::Application` +itself and are run between the `bootstrap` and `finishers`. + After this is done we go back to `Rack::Server` ### Rack: lib/rack/server.rb diff --git a/guides/source/layouts_and_rendering.md b/guides/source/layouts_and_rendering.md index 1ab841b137..5b6e5387ff 100644 --- a/guides/source/layouts_and_rendering.md +++ b/guides/source/layouts_and_rendering.md @@ -88,7 +88,7 @@ If we want to display the properties of all the books in our view, we can do so <% end %> </table> -<br /> +<br> <%= link_to "New book", new_book_path %> ``` @@ -592,7 +592,7 @@ def index end def show - @book = Book.find_by_id(params[:id]) + @book = Book.find_by(id: params[:id]) if @book.nil? render action: "index" end @@ -607,7 +607,7 @@ def index end def show - @book = Book.find_by_id(params[:id]) + @book = Book.find_by(id: params[:id]) if @book.nil? redirect_to action: :index end @@ -626,10 +626,10 @@ def index end def show - @book = Book.find_by_id(params[:id]) + @book = Book.find_by(id: params[:id]) if @book.nil? @books = Book.all - flash[:alert] = "Your book was not found" + flash.now[:alert] = "Your book was not found" render "index" end end @@ -1026,7 +1026,7 @@ You can also pass local variables into partials, making them even more powerful ```html+erb <%= form_for(zone) do |f| %> <p> - <b>Zone name</b><br /> + <b>Zone name</b><br> <%= f.text_field :name %> </p> <p> diff --git a/guides/source/migrations.md b/guides/source/migrations.md index 508e52a77c..e6d1e71f5e 100644 --- a/guides/source/migrations.md +++ b/guides/source/migrations.md @@ -314,7 +314,7 @@ will produce a migration that looks like this class AddDetailsToProducts < ActiveRecord::Migration def change add_column :products, :price, precision: 5, scale: 2 - add_reference :products, :user, polymorphic: true, index: true + add_reference :products, :supplier, polymorphic: true, index: true end end ``` @@ -376,7 +376,7 @@ create_join_table :products, :categories, column_options: {null: true} will create the `product_id` and `category_id` with the `:null` option as `true`. -You can pass the option `:table_name` with you want to customize the table +You can pass the option `:table_name` when you want to customize the table name. For example, ```ruby @@ -841,7 +841,6 @@ class AddFlagToProduct < ActiveRecord::Migration reversible do |dir| dir.up { Product.update_all flag: false } end - Product.update_all flag: false end end ``` diff --git a/guides/source/rails_application_templates.md b/guides/source/rails_application_templates.md index f8062a1f7c..0c70f379be 100644 --- a/guides/source/rails_application_templates.md +++ b/guides/source/rails_application_templates.md @@ -227,3 +227,22 @@ git :init git add: "." git commit: "-a -m 'Initial commit'" ``` + +Advanced Usage +-------------- + +The application template is evaluated in the context of a +`Rails::Generators::AppGenerator` instance. It uses the `apply` action +provided by +[Thor](https://github.com/erikhuda/thor/blob/master/lib/thor/actions.rb#L207). +This means you can extend and change the instance to match your needs. + +For example by overwriting the `source_paths` method to contain the +location of your template. Now methods like `copy_file` will accept +relative paths to your template's location. + +```ruby +def source_paths + [File.expand_path(File.dirname(__FILE__))] +end +``` diff --git a/guides/source/rails_on_rack.md b/guides/source/rails_on_rack.md index d144fba762..b1a7865d10 100644 --- a/guides/source/rails_on_rack.md +++ b/guides/source/rails_on_rack.md @@ -326,7 +326,7 @@ The following shows how to replace use `Rack::Builder` instead of the Rails supp config.middleware.clear ``` -<br /> +<br> <strong>Add a `config.ru` file to `Rails.root`</strong> ```ruby diff --git a/guides/source/testing.md b/guides/source/testing.md index 416a8b592f..62c9835fa4 100644 --- a/guides/source/testing.md +++ b/guides/source/testing.md @@ -92,7 +92,7 @@ user_<%= n %>: #### Fixtures in Action -Rails by default automatically loads all fixtures from the `test/fixtures` folder for your unit and functional test. Loading involves three steps: +Rails by default automatically loads all fixtures from the `test/fixtures` folder for your models and controllers test. Loading involves three steps: * Remove any existing data from the table corresponding to the fixture * Load the fixture data into the table @@ -116,7 +116,7 @@ email(david.girlfriend.email, david.location_tonight) Unit Testing your Models ------------------------ -In Rails, unit tests are what you write to test your models. +In Rails, models tests are what you write to test your models. For this guide we will be using Rails _scaffolding_. It will create the model, a migration, controller and views for the new resource in a single operation. It will also create a full test suite following Rails best practices. I will be using examples from this generated code and will be supplementing it with additional examples where necessary. diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md index 7e046c4659..5992e94cd8 100644 --- a/guides/source/upgrading_ruby_on_rails.md +++ b/guides/source/upgrading_ruby_on_rails.md @@ -205,16 +205,6 @@ If you are relying on the ability for external applications or Javascript to be * Rails 4.0 encrypts the contents of cookie-based sessions if `secret_key_base` has been set. Rails 3.x signed, but did not encrypt, the contents of cookie-based session. Signed cookies are "secure" in that they are verified to have been generated by your app and are tamper-proof. However, the contents can be viewed by end users, and encrypting the contents eliminates this caveat/concern without a significant performance penalty. -As described above, existing signed cookies generated with Rails 3.x will be transparently upgraded if you leave your existing `secret_token` in place and add the new `secret_key_base`. - -```ruby - # config/initializers/secret_token.rb - Myapp::Application.config.secret_token = 'existing secret token' - Myapp::Application.config.secret_key_base = 'new secret key base' -``` - -The same caveats apply here, too. You should wait to set `secret_key_base` until you have 100% of your userbase on Rails 4.x and are reasonably sure you will not need to rollback to Rails 3.x. You should also take care to make sure you are not relying on the ability to decode signed cookies generated by your app in external applications or Javascript before upgrading. - Please read [Pull Request #9978](https://github.com/rails/rails/pull/9978) for details on the move to encrypted session cookies. * Rails 4.0 removed the `ActionController::Base.asset_path` option. Use the assets pipeline feature. diff --git a/install.rb b/install.rb index 77c3e6048a..bff8fee934 100644 --- a/install.rb +++ b/install.rb @@ -5,7 +5,7 @@ if version.nil? exit(64) end -%w( activesupport activemodel activerecord actionpack actionmailer railties ).each do |framework| +%w( activesupport activemodel activerecord actionpack actionview actionmailer railties ).each do |framework| puts "Installing #{framework}..." `cd #{framework} && gem build #{framework}.gemspec && gem install #{framework}-#{version}.gem --no-ri --no-rdoc && rm #{framework}-#{version}.gem` end diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md index 00ef1ad058..cc7fecb28a 100644 --- a/railties/CHANGELOG.md +++ b/railties/CHANGELOG.md @@ -1,3 +1,36 @@ +* Fix `rake notes` to look into `*.sass` files + + *Yuri Artemev* + +* Removed deprecated `Rails.application.railties.engines`. + + *Arun Agrawal* + +* Removed deprecated threadsafe! from Rails Config. + + *Paul Nikitochkin* + +* Remove deprecated `ActiveRecord::Generators::ActiveModel#update_attributes` in + favor of `ActiveRecord::Generators::ActiveModel#update` + + *Vipul A M* + +* Remove deprecated `config.whiny_nils` option + + *Vipul A M* + +* Rename `commands/plugin_new.rb` to `commands/plugin.rb` and fix references + + *Richard Schneeman* + +* Fix `rails plugin --help` command. + + *Richard Schneeman* + +* Omit turbolinks configuration completely on skip_javascript generator option. + + *Nikita Fedyashev* + * Removed deprecated rake tasks for running tests: `rake test:uncommitted` and `rake test:recent`. diff --git a/railties/RDOC_MAIN.rdoc b/railties/RDOC_MAIN.rdoc index cadf0fb43e..aa67005b24 100644 --- a/railties/RDOC_MAIN.rdoc +++ b/railties/RDOC_MAIN.rdoc @@ -18,7 +18,7 @@ you to present the data from database rows as objects and embellish these data o with business logic methods. Although most \Rails models are backed by a database, models can also be ordinary Ruby classes, or Ruby classes that implement a set of interfaces as provided by the ActiveModel module. You can read more about Active Record in its -{README}[link:/activerecord/README.rdoc]. +{README}[link:files/activerecord/README_rdoc.html]. The Controller layer is responsible for handling incoming HTTP requests and providing a suitable response. Usually this means returning \HTML, but \Rails controllers can also @@ -29,7 +29,7 @@ In \Rails, the Controller and View layers are handled together by Action Pack. These two layers are bundled in a single package due to their heavy interdependence. This is unlike the relationship between Active Record and Action Pack, which are independent. Each of these packages can be used independently outside of \Rails. You -can read more about Action Pack in its {README}[link:/actionpack/README.rdoc]. +can read more about Action Pack in its {README}[link:files/actionpack/README_rdoc.html]. == Getting Started diff --git a/railties/lib/rails.rb b/railties/lib/rails.rb index 3a3c11d8b3..ea82327365 100644 --- a/railties/lib/rails.rb +++ b/railties/lib/rails.rb @@ -10,7 +10,6 @@ require 'active_support/core_ext/array/extract_options' require 'rails/application' require 'rails/version' -require 'rails/deprecation' require 'active_support/railtie' require 'action_dispatch/railtie' diff --git a/railties/lib/rails/api/task.rb b/railties/lib/rails/api/task.rb index c829873da4..edd2283182 100644 --- a/railties/lib/rails/api/task.rb +++ b/railties/lib/rails/api/task.rb @@ -135,12 +135,20 @@ module Rails def api_dir 'doc/rdoc' end + end + class EdgeTask < RepoTask def rails_version "master@#{`git rev-parse HEAD`[0, 7]}" end end + class StableTask < RepoTask + def rails_version + File.read('RAILS_VERSION').strip + end + end + class AppTask < Task def component_root_dir(gem_name) $:.grep(%r{#{gem_name}[\w.-]*/lib\z}).first[0..-5] diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index b5c5a6191f..6fd01ee768 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -51,6 +51,29 @@ module Rails # 10) Run config.before_eager_load and eager_load! if eager_load is true # 11) Run config.after_initialize callbacks # + # == Multiple Applications + # + # If you decide to define multiple applications, then the first application + # that is initialized will be set to +Rails.application+, unless you override + # it with a different application. + # + # To create a new application, you can instantiate a new instance of a class + # that has already been created: + # + # class Application < Rails::Application + # end + # + # first_application = Application.new + # second_application = Application.new(config: first_application.config) + # + # In the above example, the configuration from the first application was used + # to initialize the second application. You can also use the +initialize_copy+ + # on one of the applications to create a copy of the application which shares + # the configuration. + # + # If you decide to define rake tasks, runners, or initializers in an + # application other than +Rails.application+, then you must run those + # these manually. class Application < Engine autoload :Bootstrap, 'rails/application/bootstrap' autoload :Configuration, 'rails/application/configuration' @@ -61,12 +84,16 @@ module Rails class << self def inherited(base) - raise "You cannot have more than one Rails::Application" if Rails.application super - Rails.application = base.instance - Rails.application.add_lib_to_load_path! - ActiveSupport.run_load_hooks(:before_configuration, base.instance) + Rails.application ||= base.instance end + + # Makes the +new+ method public. + # + # Note that Rails::Application inherits from Rails::Engine, which + # inherits from Rails::Railtie and the +new+ method on Rails::Railtie is + # private + public :new end attr_accessor :assets, :sandbox @@ -75,14 +102,28 @@ module Rails delegate :default_url_options, :default_url_options=, to: :routes - def initialize - super + INITIAL_VARIABLES = [:config, :railties, :routes_reloader, :reloaders, + :routes, :helpers, :app_env_config] # :nodoc: + + def initialize(initial_variable_values = {}, &block) + super() @initialized = false @reloaders = [] @routes_reloader = nil @app_env_config = nil @ordered_railties = nil @railties = nil + + add_lib_to_load_path! + ActiveSupport.run_load_hooks(:before_configuration, self) + + initial_variable_values.each do |variable_name, value| + if INITIAL_VARIABLES.include?(variable_name) + instance_variable_set("@#{variable_name}", value) + end + end + + instance_eval(&block) if block_given? end # Returns true if the application is initialized. @@ -141,6 +182,30 @@ module Rails end end + # If you try to define a set of rake tasks on the instance, these will get + # passed up to the rake tasks defined on the application's class. + def rake_tasks(&block) + self.class.rake_tasks(&block) + end + + # Sends the initializers to the +initializer+ method defined in the + # Rails::Initializable module. Each Rails::Application class has its own + # set of initializers, as defined by the Initializable module. + def initializer(name, opts={}, &block) + self.class.initializer(name, opts, &block) + end + + # Sends any runner called in the instance of a new application up + # to the +runner+ method defined in Rails::Railtie. + def runner(&blk) + self.class.runner(&blk) + end + + # Sends the +isolate_namespace+ method up to the class method. + def isolate_namespace(mod) + self.class.isolate_namespace(mod) + end + ## Rails internal API # This method is called just after an application inherits from Rails::Application, @@ -158,7 +223,9 @@ module Rails # you need to load files in lib/ during the application configuration as well. def add_lib_to_load_path! #:nodoc: path = File.join config.root, 'lib' - $LOAD_PATH.unshift(path) if File.exists?(path) + if File.exists?(path) && !$LOAD_PATH.include?(path) + $LOAD_PATH.unshift(path) + end end def require_environment! #:nodoc: @@ -202,6 +269,10 @@ module Rails @config ||= Application::Configuration.new(find_root_with_flag("config.ru", Dir.pwd)) end + def config=(configuration) #:nodoc: + @config = configuration + end + def to_app #:nodoc: self end diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb index 644893d9e1..7332444ab9 100644 --- a/railties/lib/rails/application/configuration.rb +++ b/railties/lib/rails/application/configuration.rb @@ -87,16 +87,6 @@ module Rails end end - def threadsafe! - message = "config.threadsafe! is deprecated. Rails applications " \ - "behave by default as thread safe in production as long as config.cache_classes and " \ - "config.eager_load are set to true" - ActiveSupport::Deprecation.warn message - @cache_classes = true - @eager_load = true - self - end - # Loads and returns the configuration of the database. def database_configuration yaml = paths["config/database"].first @@ -150,9 +140,6 @@ module Rails end end - def whiny_nils=(*) - ActiveSupport::Deprecation.warn "config.whiny_nils option is deprecated and no longer works" - end end end end diff --git a/railties/lib/rails/cli.rb b/railties/lib/rails/cli.rb index e5341ac436..20313a2608 100644 --- a/railties/lib/rails/cli.rb +++ b/railties/lib/rails/cli.rb @@ -10,7 +10,7 @@ Signal.trap("INT") { puts; exit(1) } if ARGV.first == 'plugin' ARGV.shift - require 'rails/commands/plugin_new' + require 'rails/commands/plugin' else require 'rails/commands/application' end diff --git a/railties/lib/rails/commands.rb b/railties/lib/rails/commands.rb index 48007dd2ab..f32bf772a5 100644 --- a/railties/lib/rails/commands.rb +++ b/railties/lib/rails/commands.rb @@ -9,103 +9,9 @@ aliases = { "r" => "runner" } -help_message = <<-EOT -Usage: rails COMMAND [ARGS] - -The most common rails commands are: - generate Generate new code (short-cut alias: "g") - console Start the Rails console (short-cut alias: "c") - server Start the Rails server (short-cut alias: "s") - dbconsole Start a console for the database specified in config/database.yml - (short-cut alias: "db") - 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: - application Generate the Rails application code - 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. -EOT - - command = ARGV.shift command = aliases[command] || command -case command -when 'generate', 'destroy', 'plugin' - require 'rails/generators' - - if command == 'plugin' && ARGV.first == 'new' - require "rails/commands/plugin_new" - else - require APP_PATH - Rails.application.require_environment! - - Rails.application.load_generators - - require "rails/commands/#{command}" - end - -when 'console' - require 'rails/commands/console' - options = Rails::Console.parse_arguments(ARGV) - - # RAILS_ENV needs to be set before config/application is required - ENV['RAILS_ENV'] = options[:environment] if options[:environment] - - # shift ARGV so IRB doesn't freak - ARGV.shift if ARGV.first && ARGV.first[0] != '-' - - require APP_PATH - Rails.application.require_environment! - Rails::Console.start(Rails.application, options) - -when 'server' - # Change to the application's path if there is no config.ru file in current directory. - # This allows us to run `rails server` from other directories, but still get - # the main config.ru and properly set the tmp directory. - Dir.chdir(File.expand_path('../../', APP_PATH)) unless File.exists?(File.expand_path("config.ru")) - - require 'rails/commands/server' - Rails::Server.new.tap do |server| - # We need to require application after the server sets environment, - # otherwise the --environment option given to the server won't propagate. - require APP_PATH - Dir.chdir(Rails.application.root) - server.start - end - -when 'dbconsole' - require 'rails/commands/dbconsole' - Rails::DBConsole.start - -when 'application', 'runner' - require "rails/commands/#{command}" - -when 'new' - if %w(-h --help).include?(ARGV.first) - require 'rails/commands/application' - else - puts "Can't initialize a new Rails application within the directory of another, please change to a non-Rails directory first.\n" - puts "Type 'rails' for help." - exit(1) - end - -when '--version', '-v' - ARGV.unshift '--version' - require 'rails/commands/application' - -when '-h', '--help' - puts help_message +require 'rails/commands/commands_tasks' -else - puts "Error: Command '#{command}' not recognized" - if %x{rake #{command} --dry-run 2>&1 } && $?.success? - puts "Did you mean: `$ rake #{command}` ?\n\n" - end - puts help_message - exit(1) -end +Rails::CommandsTasks.new(ARGV).run_command!(command) diff --git a/railties/lib/rails/commands/commands_tasks.rb b/railties/lib/rails/commands/commands_tasks.rb new file mode 100644 index 0000000000..bb25d6419b --- /dev/null +++ b/railties/lib/rails/commands/commands_tasks.rb @@ -0,0 +1,170 @@ +module Rails + # This is a class which takes in a rails command and initiates the appropriate + # initiation sequence. + # + # Warning: This class mutates ARGV because some commands require manipulating + # it before they are run. + class CommandsTasks # :nodoc: + attr_reader :argv + + HELP_MESSAGE = <<-EOT +Usage: rails COMMAND [ARGS] + +The most common rails commands are: + generate Generate new code (short-cut alias: "g") + console Start the Rails console (short-cut alias: "c") + server Start the Rails server (short-cut alias: "s") + dbconsole Start a console for the database specified in config/database.yml + (short-cut alias: "db") + 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: + application Generate the Rails application code + 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. +EOT + + COMMAND_WHITELIST = %(plugin generate destroy console server dbconsole application runner new version help) + + def initialize(argv) + @argv = argv + end + + def run_command!(command) + command = parse_command(command) + if COMMAND_WHITELIST.include?(command) + send(command) + else + write_error_message(command) + end + end + + def plugin + require_command!("plugin") + end + + def generate + generate_or_destroy(:generate) + end + + def console + require_command!("console") + options = Rails::Console.parse_arguments(argv) + + # RAILS_ENV needs to be set before config/application is required + ENV['RAILS_ENV'] = options[:environment] if options[:environment] + + # shift ARGV so IRB doesn't freak + shift_argv! + + require_application_and_environment! + Rails::Console.start(Rails.application, options) + end + + def server + set_application_directory! + require_command!("server") + + Rails::Server.new.tap do |server| + # We need to require application after the server sets environment, + # otherwise the --environment option given to the server won't propagate. + require APP_PATH + Dir.chdir(Rails.application.root) + server.start + end + end + + def dbconsole + require_command!("dbconsole") + Rails::DBConsole.start + end + + def application + require_command!("application") + end + + def runner + require_command!("runner") + end + + def new + if %w(-h --help).include?(argv.first) + require_command!("application") + else + exit_with_initialization_warning! + end + end + + def version + ARGV.unshift '--version' + require_command!("application") + end + + def help + write_help_message + end + + private + + def exit_with_initialization_warning! + puts "Can't initialize a new Rails application within the directory of another, please change to a non-Rails directory first.\n" + puts "Type 'rails' for help." + exit(1) + end + + def shift_argv! + ARGV.shift if ARGV.first && ARGV.first[0] != '-' + end + + def require_command!(command) + require "rails/commands/#{command}" + end + + def generate_or_destroy(command) + require 'rails/generators' + require_application_and_environment! + Rails.application.load_generators + require "rails/commands/#{command}" + end + + # Change to the application's path if there is no config.ru file in current directory. + # This allows us to run `rails server` from other directories, but still get + # the main config.ru and properly set the tmp directory. + def set_application_directory! + Dir.chdir(File.expand_path('../../', APP_PATH)) unless File.exists?(File.expand_path("config.ru")) + end + + def require_application_and_environment! + require APP_PATH + Rails.application.require_environment! + end + + def write_help_message + puts HELP_MESSAGE + end + + def write_error_message(command) + puts "Error: Command '#{command}' not recognized" + if %x{rake #{command} --dry-run 2>&1 } && $?.success? + puts "Did you mean: `$ rake #{command}` ?\n\n" + end + write_help_message + exit(1) + end + + def parse_command(command) + case command + when '--version', '-v' + 'version' + when '--help', '-h' + 'help' + else + command + end + end + end +end diff --git a/railties/lib/rails/commands/plugin.rb b/railties/lib/rails/commands/plugin.rb new file mode 100644 index 0000000000..837fe0ec10 --- /dev/null +++ b/railties/lib/rails/commands/plugin.rb @@ -0,0 +1,9 @@ +if ARGV.first != "new" + ARGV[0] = "--help" +else + ARGV.shift +end + +require 'rails/generators' +require 'rails/generators/rails/plugin/plugin_generator' +Rails::Generators::PluginGenerator.start diff --git a/railties/lib/rails/commands/plugin_new.rb b/railties/lib/rails/commands/plugin_new.rb deleted file mode 100644 index 4d7bf3c9f3..0000000000 --- a/railties/lib/rails/commands/plugin_new.rb +++ /dev/null @@ -1,9 +0,0 @@ -if ARGV.first != "new" - ARGV[0] = "--help" -else - ARGV.shift -end - -require 'rails/generators' -require 'rails/generators/rails/plugin_new/plugin_new_generator' -Rails::Generators::PluginNewGenerator.start
\ No newline at end of file diff --git a/railties/lib/rails/configuration.rb b/railties/lib/rails/configuration.rb index 15d13d5f28..c694513960 100644 --- a/railties/lib/rails/configuration.rb +++ b/railties/lib/rails/configuration.rb @@ -1,4 +1,3 @@ -require 'active_support/deprecation' require 'active_support/ordered_options' require 'active_support/core_ext/object' require 'rails/paths' diff --git a/railties/lib/rails/engine/railties.rb b/railties/lib/rails/engine/railties.rb index 1081700cd0..595256f36a 100644 --- a/railties/lib/rails/engine/railties.rb +++ b/railties/lib/rails/engine/railties.rb @@ -9,10 +9,6 @@ module Rails ::Rails::Engine.subclasses.map(&:instance) end - def self.engines - @engines ||= ::Rails::Engine.subclasses.map(&:instance) - end - def each(*args, &block) _all.each(*args, &block) end @@ -25,5 +21,3 @@ module Rails end end end - -ActiveSupport::Deprecation.deprecate_methods(Rails::Engine::Railties, :engines) diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb index 4b767ea0c6..6b34db3e3f 100644 --- a/railties/lib/rails/generators.rb +++ b/railties/lib/rails/generators.rb @@ -225,7 +225,7 @@ module Rails rails = groups.delete("rails") rails.map! { |n| n.sub(/^rails:/, '') } rails.delete("app") - rails.delete("plugin_new") + rails.delete("plugin") print_list("rails", rails) hidden_namespaces.each { |n| groups.delete(n.to_s) } diff --git a/railties/lib/rails/generators/active_model.rb b/railties/lib/rails/generators/active_model.rb index e5373704d7..6183944bb0 100644 --- a/railties/lib/rails/generators/active_model.rb +++ b/railties/lib/rails/generators/active_model.rb @@ -1,5 +1,3 @@ -require 'active_support/deprecation' - module Rails module Generators # ActiveModel is a class to be implemented by each ORM to allow Rails to @@ -65,12 +63,6 @@ module Rails "#{name}.update(#{params})" end - def update_attributes(*args) # :nodoc: - ActiveSupport::Deprecation.warn("Calling '@orm_instance.update_attributes' " \ - "is deprecated, please use '@orm_instance.update' instead.") - update(*args) - end - # POST create # PATCH/PUT update def errors diff --git a/railties/lib/rails/generators/migration.rb b/railties/lib/rails/generators/migration.rb index cd69a017dd..3566f96f5e 100644 --- a/railties/lib/rails/generators/migration.rb +++ b/railties/lib/rails/generators/migration.rb @@ -1,15 +1,14 @@ +require 'active_support/concern' + module Rails module Generators # Holds common methods for migrations. It assumes that migrations has the # [0-9]*_name format and can be used by another frameworks (like Sequel) # just by implementing the next migration version method. module Migration + extend ActiveSupport::Concern attr_reader :migration_number, :migration_file_name, :migration_class_name - def self.included(base) #:nodoc: - base.extend ClassMethods - end - module ClassMethods def migration_lookup_at(dirname) #:nodoc: Dir.glob("#{dirname}/[0-9]*_*.rb") diff --git a/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt b/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt index d87c7b7268..c3d1578818 100644 --- a/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt +++ b/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt @@ -2,8 +2,13 @@ <html> <head> <title><%= camelized %></title> + <%- if options[:skip_javascript] -%> + <%%= stylesheet_link_tag "application", media: "all" %> + <%%= javascript_include_tag "application" %> + <%- else -%> <%%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track" => true %> <%%= javascript_include_tag "application", "data-turbolinks-track" => true %> + <%- end -%> <%%= csrf_meta_tags %> </head> <body> 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 91253d1508..cff570a631 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 @@ -20,7 +20,7 @@ Rails.application.configure do config.active_support.deprecation = :log <%- unless options.skip_active_record? -%> - # Raise an error on page load if there are pending migrations + # Raise an error on page load if there are pending migrations. config.active_record.migration_error = :page_load <%- end -%> diff --git a/railties/lib/rails/generators/rails/plugin_new/USAGE b/railties/lib/rails/generators/rails/plugin/USAGE index 9a7bf9f396..9a7bf9f396 100644 --- a/railties/lib/rails/generators/rails/plugin_new/USAGE +++ b/railties/lib/rails/generators/rails/plugin/USAGE diff --git a/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb index 850c9d5c0d..13f5472ede 100644 --- a/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb +++ b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb @@ -154,7 +154,7 @@ task default: :test end module Generators - class PluginNewGenerator < AppBase # :nodoc: + class PluginGenerator < AppBase # :nodoc: add_shared_options_for "plugin" alias_method :plugin_path, :app_path diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/%name%.gemspec b/railties/lib/rails/generators/rails/plugin/templates/%name%.gemspec index 5fdf0e1554..5fdf0e1554 100644 --- a/railties/lib/rails/generators/rails/plugin_new/templates/%name%.gemspec +++ b/railties/lib/rails/generators/rails/plugin/templates/%name%.gemspec diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/Gemfile b/railties/lib/rails/generators/rails/plugin/templates/Gemfile index 3f2b78f2fd..3f2b78f2fd 100644 --- a/railties/lib/rails/generators/rails/plugin_new/templates/Gemfile +++ b/railties/lib/rails/generators/rails/plugin/templates/Gemfile diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/MIT-LICENSE b/railties/lib/rails/generators/rails/plugin/templates/MIT-LICENSE index d7a9109894..d7a9109894 100644 --- a/railties/lib/rails/generators/rails/plugin_new/templates/MIT-LICENSE +++ b/railties/lib/rails/generators/rails/plugin/templates/MIT-LICENSE diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/README.rdoc b/railties/lib/rails/generators/rails/plugin/templates/README.rdoc index 301d647731..301d647731 100644 --- a/railties/lib/rails/generators/rails/plugin_new/templates/README.rdoc +++ b/railties/lib/rails/generators/rails/plugin/templates/README.rdoc diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/Rakefile b/railties/lib/rails/generators/rails/plugin/templates/Rakefile index 0ba899176c..0ba899176c 100644 --- a/railties/lib/rails/generators/rails/plugin_new/templates/Rakefile +++ b/railties/lib/rails/generators/rails/plugin/templates/Rakefile diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/app/controllers/%name%/application_controller.rb.tt b/railties/lib/rails/generators/rails/plugin/templates/app/controllers/%name%/application_controller.rb.tt index 448ad7f989..448ad7f989 100644 --- a/railties/lib/rails/generators/rails/plugin_new/templates/app/controllers/%name%/application_controller.rb.tt +++ b/railties/lib/rails/generators/rails/plugin/templates/app/controllers/%name%/application_controller.rb.tt diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/app/helpers/%name%/application_helper.rb.tt b/railties/lib/rails/generators/rails/plugin/templates/app/helpers/%name%/application_helper.rb.tt index 40ae9f52c2..40ae9f52c2 100644 --- a/railties/lib/rails/generators/rails/plugin_new/templates/app/helpers/%name%/application_helper.rb.tt +++ b/railties/lib/rails/generators/rails/plugin/templates/app/helpers/%name%/application_helper.rb.tt diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/app/mailers/.empty_directory b/railties/lib/rails/generators/rails/plugin/templates/app/mailers/.empty_directory index e69de29bb2..e69de29bb2 100644 --- a/railties/lib/rails/generators/rails/plugin_new/templates/app/mailers/.empty_directory +++ b/railties/lib/rails/generators/rails/plugin/templates/app/mailers/.empty_directory diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/app/models/.empty_directory b/railties/lib/rails/generators/rails/plugin/templates/app/models/.empty_directory index e69de29bb2..e69de29bb2 100644 --- a/railties/lib/rails/generators/rails/plugin_new/templates/app/models/.empty_directory +++ b/railties/lib/rails/generators/rails/plugin/templates/app/models/.empty_directory diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/app/views/layouts/%name%/application.html.erb.tt b/railties/lib/rails/generators/rails/plugin/templates/app/views/layouts/%name%/application.html.erb.tt index 1d380420b4..1d380420b4 100644 --- a/railties/lib/rails/generators/rails/plugin_new/templates/app/views/layouts/%name%/application.html.erb.tt +++ b/railties/lib/rails/generators/rails/plugin/templates/app/views/layouts/%name%/application.html.erb.tt diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/bin/rails.tt b/railties/lib/rails/generators/rails/plugin/templates/bin/rails.tt index c8de9f3e0f..c8de9f3e0f 100644 --- a/railties/lib/rails/generators/rails/plugin_new/templates/bin/rails.tt +++ b/railties/lib/rails/generators/rails/plugin/templates/bin/rails.tt diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/config/routes.rb b/railties/lib/rails/generators/rails/plugin/templates/config/routes.rb index 8e158d5831..8e158d5831 100644 --- a/railties/lib/rails/generators/rails/plugin_new/templates/config/routes.rb +++ b/railties/lib/rails/generators/rails/plugin/templates/config/routes.rb diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/gitignore b/railties/lib/rails/generators/rails/plugin/templates/gitignore index 086d87818a..086d87818a 100644 --- a/railties/lib/rails/generators/rails/plugin_new/templates/gitignore +++ b/railties/lib/rails/generators/rails/plugin/templates/gitignore diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/lib/%name%.rb b/railties/lib/rails/generators/rails/plugin/templates/lib/%name%.rb index 40c074cced..40c074cced 100644 --- a/railties/lib/rails/generators/rails/plugin_new/templates/lib/%name%.rb +++ b/railties/lib/rails/generators/rails/plugin/templates/lib/%name%.rb diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/lib/%name%/engine.rb b/railties/lib/rails/generators/rails/plugin/templates/lib/%name%/engine.rb index 967668fe66..967668fe66 100644 --- a/railties/lib/rails/generators/rails/plugin_new/templates/lib/%name%/engine.rb +++ b/railties/lib/rails/generators/rails/plugin/templates/lib/%name%/engine.rb diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/lib/%name%/version.rb b/railties/lib/rails/generators/rails/plugin/templates/lib/%name%/version.rb index ef07ef2e19..ef07ef2e19 100644 --- a/railties/lib/rails/generators/rails/plugin_new/templates/lib/%name%/version.rb +++ b/railties/lib/rails/generators/rails/plugin/templates/lib/%name%/version.rb diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/lib/tasks/%name%_tasks.rake b/railties/lib/rails/generators/rails/plugin/templates/lib/tasks/%name%_tasks.rake index 7121f5ae23..7121f5ae23 100644 --- a/railties/lib/rails/generators/rails/plugin_new/templates/lib/tasks/%name%_tasks.rake +++ b/railties/lib/rails/generators/rails/plugin/templates/lib/tasks/%name%_tasks.rake diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/rails/application.rb b/railties/lib/rails/generators/rails/plugin/templates/rails/application.rb index 310c975262..310c975262 100644 --- a/railties/lib/rails/generators/rails/plugin_new/templates/rails/application.rb +++ b/railties/lib/rails/generators/rails/plugin/templates/rails/application.rb diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/rails/boot.rb b/railties/lib/rails/generators/rails/plugin/templates/rails/boot.rb index ef360470a3..ef360470a3 100644 --- a/railties/lib/rails/generators/rails/plugin_new/templates/rails/boot.rb +++ b/railties/lib/rails/generators/rails/plugin/templates/rails/boot.rb diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/rails/javascripts.js b/railties/lib/rails/generators/rails/plugin/templates/rails/javascripts.js index 5bc2e1c8b5..5bc2e1c8b5 100644 --- a/railties/lib/rails/generators/rails/plugin_new/templates/rails/javascripts.js +++ b/railties/lib/rails/generators/rails/plugin/templates/rails/javascripts.js diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/rails/routes.rb b/railties/lib/rails/generators/rails/plugin/templates/rails/routes.rb index 730ee31c3d..730ee31c3d 100644 --- a/railties/lib/rails/generators/rails/plugin_new/templates/rails/routes.rb +++ b/railties/lib/rails/generators/rails/plugin/templates/rails/routes.rb diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/rails/stylesheets.css b/railties/lib/rails/generators/rails/plugin/templates/rails/stylesheets.css index 3192ec897b..3192ec897b 100644 --- a/railties/lib/rails/generators/rails/plugin_new/templates/rails/stylesheets.css +++ b/railties/lib/rails/generators/rails/plugin/templates/rails/stylesheets.css diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/test/%name%_test.rb b/railties/lib/rails/generators/rails/plugin/templates/test/%name%_test.rb index 0a8bbd4aaf..0a8bbd4aaf 100644 --- a/railties/lib/rails/generators/rails/plugin_new/templates/test/%name%_test.rb +++ b/railties/lib/rails/generators/rails/plugin/templates/test/%name%_test.rb diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/test/integration/navigation_test.rb b/railties/lib/rails/generators/rails/plugin/templates/test/integration/navigation_test.rb index 824caecb24..824caecb24 100644 --- a/railties/lib/rails/generators/rails/plugin_new/templates/test/integration/navigation_test.rb +++ b/railties/lib/rails/generators/rails/plugin/templates/test/integration/navigation_test.rb diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/test/test_helper.rb b/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb index 1e26a313cd..1e26a313cd 100644 --- a/railties/lib/rails/generators/rails/plugin_new/templates/test/test_helper.rb +++ b/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb diff --git a/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml b/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml index 90a92e6982..f19e9d1d87 100644 --- a/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml +++ b/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml @@ -1,4 +1,4 @@ -# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html <% unless attributes.empty? -%> <% %w(one two).each do |name| %> <%= name %>: diff --git a/railties/lib/rails/info.rb b/railties/lib/rails/info.rb index f06ce659c5..5b69605b51 100644 --- a/railties/lib/rails/info.rb +++ b/railties/lib/rails/info.rb @@ -61,8 +61,10 @@ module Rails end end - # The Ruby version and platform, e.g. "1.8.2 (powerpc-darwin8.2.0)". - property 'Ruby version', "#{RUBY_VERSION} (#{RUBY_PLATFORM})" + # The Ruby version and platform, e.g. "2.0.0-p247 (x86_64-darwin12.4.0)". + property 'Ruby version' do + "#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL} (#{RUBY_PLATFORM})" + end # The RubyGems version, if it's installed. property 'RubyGems version' do diff --git a/railties/lib/rails/source_annotation_extractor.rb b/railties/lib/rails/source_annotation_extractor.rb index 2cbb0a435c..290634290f 100644 --- a/railties/lib/rails/source_annotation_extractor.rb +++ b/railties/lib/rails/source_annotation_extractor.rb @@ -82,7 +82,7 @@ class SourceAnnotationExtractor case item when /\.(builder|rb|coffee|rake)$/ /#\s*(#{tag}):?\s*(.*)$/ - when /\.(css|scss|js)$/ + when /\.(css|scss|sass|js)$/ /\/\/\s*(#{tag}):?\s*(.*)$/ when /\.erb$/ /<%\s*#\s*(#{tag}):?\s*(.*?)\s*%>/ diff --git a/railties/lib/rails/tasks/engine.rake b/railties/lib/rails/tasks/engine.rake index 70370be3f5..16ad1bfc84 100644 --- a/railties/lib/rails/tasks/engine.rake +++ b/railties/lib/rails/tasks/engine.rake @@ -26,7 +26,7 @@ namespace :db do desc "Display status of migrations" app_task "migrate:status" - desc 'Create the database from config/database.yml for the current Rails.env (use db:create:all to create all dbs in the config)' + desc 'Create the database from config/database.yml for the current Rails.env (use db:create:all to create all databases in the config)' app_task "create" app_task "create:all" diff --git a/railties/lib/rails/test_unit/sub_test_task.rb b/railties/lib/rails/test_unit/sub_test_task.rb index a463380e2d..d9bffba4d7 100644 --- a/railties/lib/rails/test_unit/sub_test_task.rb +++ b/railties/lib/rails/test_unit/sub_test_task.rb @@ -2,13 +2,53 @@ require 'rake/testtask' module Rails class TestTask < Rake::TestTask # :nodoc: all + # A utility class which is used primarily in "rails/test_unit/testing.rake" + # to help define rake tasks corresponding to <tt>rake test</tt>. + # + # This class takes a TestInfo class and defines the appropriate rake task + # based on the information, then invokes it. + class TestCreator + def initialize(info) + @info = info + end + + def invoke_rake_task + if @info.files.any? + create_and_run_single_test + reset_application_tasks + else + Rake::Task[ENV['TEST'] ? 'test:single' : 'test:run'].invoke + end + end + + private + + def create_and_run_single_test + Rails::TestTask.new('test:single') { |t| + t.test_files = @info.files + } + ENV['TESTOPTS'] ||= @info.opts + Rake::Task['test:single'].invoke + end + + def reset_application_tasks + Rake.application.top_level_tasks.replace @info.tasks + end + end + + # This is a utility class used by the <tt>TestTask::TestCreator</tt> class. + # This class takes a set of test tasks and checks to see if they correspond + # to test files (or can be transformed into test files). Calling <tt>files</tt> + # provides the set of test files and is used when initializing tests after + # a call to <tt>rake test</tt>. class TestInfo def initialize(tasks) @tasks = tasks + @files = nil end def files - @tasks.map { |task| + @files ||= @tasks.map { |task| [task, translate(task)].find { |file| test_file?(file) } }.compact end @@ -53,8 +93,9 @@ module Rails end end - def self.test_info(tasks) - TestInfo.new tasks + def self.test_creator(tasks) + info = TestInfo.new(tasks) + TestCreator.new(info) end def initialize(name = :test) diff --git a/railties/lib/rails/test_unit/testing.rake b/railties/lib/rails/test_unit/testing.rake index 2e23999a91..3aa33d1bba 100644 --- a/railties/lib/rails/test_unit/testing.rake +++ b/railties/lib/rails/test_unit/testing.rake @@ -1,24 +1,12 @@ require 'rbconfig' require 'rake/testtask' require 'rails/test_unit/sub_test_task' -require 'active_support/deprecation' task default: :test desc 'Runs test:units, test:functionals, test:integration together' task :test do - info = Rails::TestTask.test_info Rake.application.top_level_tasks - if info.files.any? - Rails::TestTask.new('test:single') { |t| - t.test_files = info.files - } - ENV['TESTOPTS'] ||= info.opts - Rake.application.top_level_tasks.replace info.tasks - - Rake::Task['test:single'].invoke - else - Rake::Task[ENV['TEST'] ? 'test:single' : 'test:run'].invoke - end + Rails::TestTask.test_creator(Rake.application.top_level_tasks).invoke_rake_task end namespace :test do diff --git a/railties/test/application/initializers/frameworks_test.rb b/railties/test/application/initializers/frameworks_test.rb index 33659db927..83104acf3c 100644 --- a/railties/test/application/initializers/frameworks_test.rb +++ b/railties/test/application/initializers/frameworks_test.rb @@ -182,7 +182,7 @@ module ApplicationTests end require "#{app_path}/config/environment" ActiveRecord::Base.connection.drop_table("posts") # force drop posts table for test. - assert ActiveRecord::Base.connection.schema_cache.tables["posts"] + assert ActiveRecord::Base.connection.schema_cache.tables("posts") end test "expire schema cache dump" do @@ -192,7 +192,7 @@ module ApplicationTests end silence_warnings { require "#{app_path}/config/environment" - assert !ActiveRecord::Base.connection.schema_cache.tables["posts"] + assert !ActiveRecord::Base.connection.schema_cache.tables("posts") } end diff --git a/railties/test/application/loading_test.rb b/railties/test/application/loading_test.rb index f05eade81f..4f30f30f95 100644 --- a/railties/test/application/loading_test.rb +++ b/railties/test/application/loading_test.rb @@ -36,7 +36,7 @@ class LoadingTest < ActiveSupport::TestCase test "models without table do not panic on scope definitions when loaded" do app_file "app/models/user.rb", <<-MODEL class User < ActiveRecord::Base - default_scope where(published: true) + default_scope { where(published: true) } end MODEL diff --git a/railties/test/application/multiple_applications_test.rb b/railties/test/application/multiple_applications_test.rb new file mode 100644 index 0000000000..03c343c475 --- /dev/null +++ b/railties/test/application/multiple_applications_test.rb @@ -0,0 +1,148 @@ +require 'isolation/abstract_unit' + +module ApplicationTests + class MultipleApplicationsTest < ActiveSupport::TestCase + include ActiveSupport::Testing::Isolation + + def setup + build_app(initializers: true) + boot_rails + require "#{rails_root}/config/environment" + end + + def teardown + teardown_app + end + + def test_cloning_an_application_makes_a_shallow_copy_of_config + clone = Rails.application.clone + + assert_equal Rails.application.config, clone.config, "The cloned application should get a copy of the config" + assert_equal Rails.application.config.secret_key_base, clone.config.secret_key_base, "The base secret key on the config should be the same" + end + + def test_initialization_of_multiple_copies_of_same_application + application1 = AppTemplate::Application.new + application2 = AppTemplate::Application.new + + assert_not_equal Rails.application.object_id, application1.object_id, "New applications should not be the same as the original application" + assert_not_equal Rails.application.object_id, application2.object_id, "New applications should not be the same as the original application" + end + + def test_initialization_of_application_with_previous_config + application1 = AppTemplate::Application.new(config: Rails.application.config) + application2 = AppTemplate::Application.new + + assert_equal Rails.application.config, application1.config, "Creating a new application while setting an initial config should result in the same config" + assert_not_equal Rails.application.config, application2.config, "New applications without setting an initial config should not have the same config" + end + + def test_initialization_of_application_with_previous_railties + application1 = AppTemplate::Application.new(railties: Rails.application.railties) + application2 = AppTemplate::Application.new + + assert_equal Rails.application.railties, application1.railties + assert_not_equal Rails.application.railties, application2.railties + end + + def test_initialize_new_application_with_all_previous_initialization_variables + application1 = AppTemplate::Application.new( + config: Rails.application.config, + railties: Rails.application.railties, + routes_reloader: Rails.application.routes_reloader, + reloaders: Rails.application.reloaders, + routes: Rails.application.routes, + helpers: Rails.application.helpers, + app_env_config: Rails.application.env_config + ) + + assert_equal Rails.application.config, application1.config + assert_equal Rails.application.railties, application1.railties + assert_equal Rails.application.routes_reloader, application1.routes_reloader + assert_equal Rails.application.reloaders, application1.reloaders + assert_equal Rails.application.routes, application1.routes + assert_equal Rails.application.helpers, application1.helpers + assert_equal Rails.application.env_config, application1.env_config + end + + def test_rake_tasks_defined_on_different_applications_go_to_the_same_class + $run_count = 0 + + application1 = AppTemplate::Application.new + application1.rake_tasks do + $run_count += 1 + end + + application2 = AppTemplate::Application.new + application2.rake_tasks do + $run_count += 1 + end + + require "#{app_path}/config/environment" + + assert_equal 0, $run_count, "The count should stay at zero without any calls to the rake tasks" + require 'rake' + require 'rake/testtask' + require 'rdoc/task' + Rails.application.load_tasks + assert_equal 2, $run_count, "Calling a rake task should result in two increments to the count" + end + + def test_multiple_applications_can_be_initialized + assert_nothing_raised { AppTemplate::Application.new } + end + + def test_initializers_run_on_different_applications_go_to_the_same_class + application1 = AppTemplate::Application.new + $run_count = 0 + + AppTemplate::Application.initializer :init0 do + $run_count += 1 + end + + application1.initializer :init1 do + $run_count += 1 + end + + AppTemplate::Application.new.initializer :init2 do + $run_count += 1 + end + + assert_equal 0, $run_count, "Without loading the initializers, the count should be 0" + + # Set config.eager_load to false so that a eager_load warning doesn't pop up + AppTemplate::Application.new { config.eager_load = false }.initialize! + + assert_equal 3, $run_count, "There should have been three initializers that incremented the count" + end + + def test_runners_run_on_different_applications_go_to_the_same_class + $run_count = 0 + AppTemplate::Application.runner { $run_count += 1 } + AppTemplate::Application.new.runner { $run_count += 1 } + + assert_equal 0, $run_count, "Without loading the runners, the count should be 0" + Rails.application.load_runner + assert_equal 2, $run_count, "There should have been two runners that increment the count" + end + + def test_isolate_namespace_on_an_application + assert_nil Rails.application.railtie_namespace, "Before isolating namespace, the railtie namespace should be nil" + Rails.application.isolate_namespace(AppTemplate) + assert_equal Rails.application.railtie_namespace, AppTemplate, "After isolating namespace, we should have a namespace" + end + + def test_inserting_configuration_into_application + app = AppTemplate::Application.new(config: Rails.application.config) + new_config = Rails::Application::Configuration.new("root_of_application") + new_config.secret_key_base = "some_secret_key_dude" + app.config.secret_key_base = "a_different_secret_key" + + assert_equal "a_different_secret_key", app.config.secret_key_base, "The configuration's secret key should be set." + app.config = new_config + assert_equal "some_secret_key_dude", app.config.secret_key_base, "The configuration's secret key should have changed." + assert_equal "root_of_application", app.config.root, "The root should have changed to the new config's root." + assert_equal new_config, app.config, "The application's config should have changed to the new config." + end + end +end diff --git a/railties/test/application/rake/notes_test.rb b/railties/test/application/rake/notes_test.rb index 3508f4225a..9a92c5f6ff 100644 --- a/railties/test/application/rake/notes_test.rb +++ b/railties/test/application/rake/notes_test.rb @@ -24,6 +24,7 @@ module ApplicationTests app_file "app/assets/javascripts/application.js", "// TODO: note in js" app_file "app/assets/stylesheets/application.css", "// TODO: note in css" app_file "app/assets/stylesheets/application.css.scss", "// TODO: note in scss" + app_file "app/assets/stylesheets/application.css.sass", "// TODO: note in sass" app_file "app/controllers/application_controller.rb", 1000.times.map { "" }.join("\n") << "# TODO: note in ruby" app_file "lib/tasks/task.rake", "# TODO: note in rake" @@ -46,9 +47,10 @@ module ApplicationTests assert_match(/note in js/, output) assert_match(/note in css/, output) assert_match(/note in scss/, output) + assert_match(/note in sass/, output) assert_match(/note in rake/, output) - assert_equal 9, lines.size + assert_equal 10, lines.size lines.each do |line| assert_equal 4, line[0].size diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index e992534938..42b6275932 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -53,9 +53,10 @@ class AppGeneratorTest < Rails::Generators::TestCase def test_assets run_generator - assert_file "app/views/layouts/application.html.erb", /stylesheet_link_tag\s+"application"/ - assert_file "app/views/layouts/application.html.erb", /javascript_include_tag\s+"application"/ - assert_file "app/assets/stylesheets/application.css" + + assert_file("app/views/layouts/application.html.erb", /stylesheet_link_tag\s+"application", media: "all", "data-turbolinks-track" => true/) + assert_file("app/views/layouts/application.html.erb", /javascript_include_tag\s+"application", "data-turbolinks-track" => true/) + assert_file("app/assets/stylesheets/application.css") end def test_invalid_application_name_raises_an_error @@ -295,6 +296,10 @@ class AppGeneratorTest < Rails::Generators::TestCase assert_file "app/assets/javascripts/application.js" do |contents| assert_no_match %r{^//=\s+require\s}, contents end + assert_file "app/views/layouts/application.html.erb" do |contents| + assert_match(/stylesheet_link_tag\s+"application", media: "all" %>/, contents) + assert_match(/javascript_include_tag\s+"application" \%>/, contents) + end assert_file "Gemfile" do |content| assert_match(/coffee-rails/, content) end diff --git a/railties/test/generators/plugin_new_generator_test.rb b/railties/test/generators/plugin_generator_test.rb index 32c7612a8f..068eb66bc6 100644 --- a/railties/test/generators/plugin_new_generator_test.rb +++ b/railties/test/generators/plugin_generator_test.rb @@ -1,5 +1,5 @@ require 'generators/generators_test_helper' -require 'rails/generators/rails/plugin_new/plugin_new_generator' +require 'rails/generators/rails/plugin/plugin_generator' require 'generators/shared_generator_tests' DEFAULT_PLUGIN_FILES = %w( @@ -18,7 +18,7 @@ DEFAULT_PLUGIN_FILES = %w( test/dummy ) -class PluginNewGeneratorTest < Rails::Generators::TestCase +class PluginGeneratorTest < Rails::Generators::TestCase include GeneratorsTestHelper destination File.join(Rails.root, "tmp/bukkits") arguments [destination_root] diff --git a/railties/test/generators_test.rb b/railties/test/generators_test.rb index 361784f509..5130b285a9 100644 --- a/railties/test/generators_test.rb +++ b/railties/test/generators_test.rb @@ -106,7 +106,7 @@ class GeneratorsTest < Rails::Generators::TestCase def test_rails_generators_help_does_not_include_app_nor_plugin_new output = capture(:stdout){ Rails::Generators.help } assert_no_match(/app/, output) - assert_no_match(/plugin_new/, output) + assert_no_match(/[^:]plugin/, output) end def test_rails_generators_with_others_information diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb index 50253fe2bc..d095535abd 100644 --- a/railties/test/railties/engine_test.rb +++ b/railties/test/railties/engine_test.rb @@ -1236,12 +1236,6 @@ YAML assert_equal '/foo/bukkits/bukkit', last_response.body end - test "engines method is properly deprecated" do - boot_rails - - assert_deprecated { app.railties.engines } - end - private def app Rails.application |