diff options
186 files changed, 1148 insertions, 2291 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) diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 05495179c3..ee3ff907cc 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,3 +1,24 @@ +* 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/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/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/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb index 1f899a434c..19332a2bc2 100644 --- a/actionpack/lib/action_dispatch/testing/integration.rb +++ b/actionpack/lib/action_dispatch/testing/integration.rb @@ -489,10 +489,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/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/actionview/CHANGELOG.md b/actionview/CHANGELOG.md index 31e123d916..921ea2be6f 100644 --- a/actionview/CHANGELOG.md +++ b/actionview/CHANGELOG.md @@ -1,3 +1,21 @@ +* 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/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/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/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/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 0d7efab04b..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.. @@ -289,7 +289,7 @@ module ActiveModel # # => NameIsInvalid: name is invalid # # person.errors.messages # => {} - def add(attribute, message = nil, options = {}) + def add(attribute, message = :invalid, options = {}) message = normalize_message(attribute, message, options) if exception = options[:strict] exception = ActiveModel::StrictValidationFailed if exception == true @@ -331,7 +331,7 @@ module ActiveModel # # person.errors.add :name, :blank # person.errors.added? :name, :blank # => true - def added?(attribute, message = nil, options = {}) + def added?(attribute, message = :invalid, options = {}) message = normalize_message(attribute, message, options) self[attribute].include? message end @@ -437,8 +437,6 @@ module ActiveModel private def normalize_message(attribute, message, options) - message ||= :invalid - case message when Symbol generate_message(attribute, message, options.except(*CALLBACKS_OPTIONS)) 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..cf65f6dcd3 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,128 @@ +* 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/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/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..2fd2ea896b 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,46 @@ 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 + } + 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 +140,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 +183,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 +193,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..684fe2dc05 100644 --- a/activerecord/lib/active_record/attribute_methods/read.rb +++ b/activerecord/lib/active_record/attribute_methods/read.rb @@ -33,8 +33,11 @@ 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). + # 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 @@ -49,13 +52,21 @@ module ActiveRecord # key the @attributes_cache in read_attribute. 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__#{safe_name} - read_attribute(AttrNames::ATTR_#{safe_name}) { |n| missing_attribute(n, caller) } + def #{temp_method} + name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{safe_name} + read_attribute(name) { |n| missing_attribute(n, caller) } end - alias_method #{name.inspect}, :__temp__#{safe_name} - undef_method :__temp__#{safe_name} STR + + generated_attribute_methods.module_eval do + alias_method name, temp_method + undef_method temp_method + 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..9c7f643283 100644 --- a/activerecord/lib/active_record/attribute_methods/write.rb +++ b/activerecord/lib/active_record/attribute_methods/write.rb @@ -14,9 +14,12 @@ module ActiveRecord # this code. 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) - write_attribute(AttrNames::ATTR_#{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}= 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..d93b79d57f 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. 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/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/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/relation.rb b/activerecord/lib/active_record/relation.rb index d37471e9ad..612f376c55 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,15 @@ 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 + if eager_loading? + join_dependency = construct_join_dependency + relation = construct_relation_for_association_find(join_dependency) + klass.connection.to_sql(relation.arel, relation.bind_values) + else + klass.connection.to_sql(arel, bind_values.dup) + end + end end # Returns a hash of where conditions. @@ -512,12 +517,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 +569,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 +588,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 +613,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..bad5886cde 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -32,7 +32,7 @@ module ActiveRecord # end # # ==== Variations of +find+ - # + # # Person.where(name: 'Spartacus', rating: 4) # # returns a chainable list (which can be empty). # @@ -49,7 +49,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 +124,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 @@ -211,7 +211,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 +353,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..e7d5e6ce84 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? 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/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/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..0f9af8a0c3 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 @@ -537,25 +499,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 +734,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 +773,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/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/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/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..5c7751b445 100644 --- a/activerecord/test/cases/scoping/default_scoping_test.rb +++ b/activerecord/test/cases/scoping/default_scoping_test.rb @@ -153,9 +153,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 diff --git a/activerecord/test/cases/scoping/named_scoping_test.rb b/activerecord/test/cases/scoping/named_scoping_test.rb index afe32af1d1..abd0b8621a 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? @@ -450,14 +445,13 @@ class NamedScopingTest < ActiveRecord::TestCase 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/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..061f296dc5 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,3 +1,35 @@ +* 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 favour 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/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/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/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/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/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/deprecation.rb b/activesupport/lib/active_support/deprecation.rb index 6c15fffc0f..0281c9222e 100644 --- a/activesupport/lib/active_support/deprecation.rb +++ b/activesupport/lib/active_support/deprecation.rb @@ -25,11 +25,11 @@ module ActiveSupport include Reporting include MethodWrapper - # The version the deprecated behavior will be removed, by default. + # The version number in which the deprecated behavior will be removed, by default. attr_accessor :deprecation_horizon - # It accepts two parameters on initialization. The first is an version of library - # and the second is an library name + # It accepts two parameters on initialization. The first is a version of library + # and the second is a library name # # ActiveSupport::Deprecation.new('2.0', 'MyLibrary') def initialize(deprecation_horizon = '4.1', gem_name = 'Rails') 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/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..0857796036 100644 --- a/activesupport/test/core_ext/hash_ext_test.rb +++ b/activesupport/test/core_ext/hash_ext_test.rb @@ -701,12 +701,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/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/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/bug_report_templates/active_record_gem.rb b/guides/bug_report_templates/active_record_gem.rb index 2e604bec47..a8868a1877 100644 --- a/guides/bug_report_templates/active_record_gem.rb +++ b/guides/bug_report_templates/active_record_gem.rb @@ -1,5 +1,5 @@ # Activate the gem you are reporting the issue against. -gem 'activerecord', '3.2.13' +gem 'activerecord', '4.0.0' require 'active_record' require 'minitest/autorun' require 'logger' 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/action_controller_overview.md b/guides/source/action_controller_overview.md index 6a91418e1f..f1600fe458 100644 --- a/guides/source/action_controller_overview.md +++ b/guides/source/action_controller_overview.md @@ -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/active_record_basics.md b/guides/source/active_record_basics.md index 1f25c6ae95..d9fb20f3bf 100644 --- a/guides/source/active_record_basics.md +++ b/guides/source/active_record_basics.md @@ -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 ``` diff --git a/guides/source/active_record_callbacks.md b/guides/source/active_record_callbacks.md index bb42fab101..01401cc340 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` 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/debugging_rails_applications.md b/guides/source/debugging_rails_applications.md index 0860e3119d..c17aa64cac 100644 --- a/guides/source/debugging_rails_applications.md +++ b/guides/source/debugging_rails_applications.md @@ -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/getting_started.md b/guides/source/getting_started.md index 455907535b..26360e815b 100644 --- a/guides/source/getting_started.md +++ b/guides/source/getting_started.md @@ -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,15 +252,15 @@ 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 ``` @@ -612,11 +612,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 ``` @@ -767,7 +772,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 +780,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 +915,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 +933,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 @@ -1062,7 +1079,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 +1105,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 @@ -1303,9 +1320,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 ``` @@ -1527,10 +1549,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 +1562,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 9e5278c92b..062da3877b 100644 --- a/guides/source/i18n.md +++ b/guides/source/i18n.md @@ -283,7 +283,7 @@ def extract_locale_from_accept_language_header end ``` -Of course, in a production environment you would need much more robust code, and could use a plugin such as Iain Hecker's [http_accept_language](https://github.com/iain/http_accept_language/tree/master or even Rack middleware such as Ryan Tomayko's [locale](https://github.com/rack/rack-contrib/blob/master/lib/rack/contrib/locale.rb). +Of course, in a production environment you would need much more robust code, and could use a plugin such as Iain Hecker's [http_accept_language](https://github.com/iain/http_accept_language/tree/master) or even Rack middleware such as Ryan Tomayko's [locale](https://github.com/rack/rack-contrib/blob/master/lib/rack/contrib/locale.rb). #### Using GeoIP (or Similar) Database 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..5908801bc9 100644 --- a/guides/source/layouts_and_rendering.md +++ b/guides/source/layouts_and_rendering.md @@ -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 diff --git a/guides/source/migrations.md b/guides/source/migrations.md index 508e52a77c..035f9499de 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 ``` 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/railties/CHANGELOG.md b/railties/CHANGELOG.md index 00ef1ad058..a3f5796b2c 100644 --- a/railties/CHANGELOG.md +++ b/railties/CHANGELOG.md @@ -1,3 +1,19 @@ +* 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/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..0c4245b55b 100644 --- a/railties/lib/rails/application/configuration.rb +++ b/railties/lib/rails/application/configuration.rb @@ -150,9 +150,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..3e8163490f 100644 --- a/railties/lib/rails/commands.rb +++ b/railties/lib/rails/commands.rb @@ -35,19 +35,17 @@ command = ARGV.shift command = aliases[command] || command case command -when 'generate', 'destroy', 'plugin' +when 'plugin' + require "rails/commands/plugin" +when 'generate', 'destroy' require 'rails/generators' - if command == 'plugin' && ARGV.first == 'new' - require "rails/commands/plugin_new" - else - require APP_PATH - Rails.application.require_environment! + require APP_PATH + Rails.application.require_environment! - Rails.application.load_generators + Rails.application.load_generators - require "rails/commands/#{command}" - end + require "rails/commands/#{command}" when 'console' require 'rails/commands/console' 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/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/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/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 f1f20293f0..e35eccc2eb 100644 --- a/railties/lib/rails/test_unit/testing.rake +++ b/railties/lib/rails/test_unit/testing.rake @@ -3,35 +3,11 @@ require 'rake/testtask' require 'rails/test_unit/sub_test_task' require 'active_support/deprecation' -# Recreated here from Active Support because :uncommitted needs it before Rails is available -module Kernel - remove_method :silence_stderr # Removing old method to prevent method redefined warning - def silence_stderr - old_stderr = STDERR.dup - STDERR.reopen(RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ ? 'NUL:' : '/dev/null') - STDERR.sync = true - yield - ensure - STDERR.reopen(old_stderr) - end -end - 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 @@ -52,11 +28,6 @@ namespace :test do task :db => %w[db:test:prepare test:all] end - # Display deprecation message - task :deprecated do - ActiveSupport::Deprecation.warn "`rake #{ARGV.first}` is deprecated with no replacement." - end - Rails::TestTask.new(single: "test:prepare") ["models", "helpers", "controllers", "mailers", "integration"].each do |name| 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/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 |