diff options
172 files changed, 1167 insertions, 1067 deletions
@@ -4,7 +4,7 @@ gemspec gem 'arel', github: 'rails/arel', branch: 'master' -gem 'mocha', '>= 0.11.2', :require => false +gem 'mocha', '>= 0.11.2', require: false gem 'rack-test', github: 'brynary/rack-test' gem 'rack-cache', '~> 1.2' gem 'bcrypt-ruby', '~> 3.0.0' @@ -28,7 +28,7 @@ group :doc do # for some weeks unapplied. As a temporary solution # this is our own fork with the fix. gem 'sdoc', github: 'fxn/sdoc' - gem 'redcarpet', '~> 2.1.1', :platforms => :ruby + gem 'redcarpet', '~> 2.2.2', platforms: :ruby gem 'w3c_validators' end @@ -42,12 +42,11 @@ instance_eval File.read local_gemfile if File.exists? local_gemfile platforms :mri do group :test do gem 'ruby-prof', '~> 0.11.2' if RUBY_VERSION < '2.0' - gem 'debugger' if !ENV['TRAVIS'] && RUBY_VERSION < '2.0' && RUBY_PATCHLEVEL < 286 + gem 'debugger' if !ENV['TRAVIS'] && RUBY_VERSION < '2.0' end end platforms :ruby do - gem 'json' gem 'yajl-ruby' gem 'nokogiri', '>= 1.4.5' diff --git a/README.rdoc b/README.rdoc index c9fb595efb..87ec64e3b5 100644 --- a/README.rdoc +++ b/README.rdoc @@ -6,29 +6,29 @@ database-backed web applications according to the {Model-View-Controller (MVC)}[ Understanding the MVC pattern is key to understanding Rails. MVC divides your application into three layers, each with a specific responsibility. -The View layer is composed of "templates" that are responsible for providing +The View layer is composed of "templates" that are responsible for providing appropriate representations of your application's resources. Templates can come in a variety of formats, but most view templates are \HTML with embedded Ruby -code (.erb files). +code (.erb files). -The Model layer represents your domain model (such as Account, Product, Person, Post) -and encapsulates the business logic that is specific to your application. In Rails, +The Model layer represents your domain model (such as Account, Product, Person, Post) +and encapsulates the business logic that is specific to your application. In Rails, database-backed model classes are derived from ActiveRecord::Base. Active Record allows -you to present the data from database rows as objects and embellish these data objects -with business logic methods. Although most Rails models are backed by a database, models +you to present the data from database rows as objects and embellish these data objects +with business logic methods. Although most Rails models are backed by a database, models can also be ordinary Ruby classes, or Ruby classes that implement a set of interfaces as provided by the ActiveModel module. You can read more about Active Record in its {README}[link:/rails/rails/blob/master/activerecord/README.rdoc]. -The Controller layer is responsible for handling incoming HTTP requests and providing a +The Controller layer is responsible for handling incoming HTTP requests and providing a suitable response. Usually this means returning \HTML, but Rails controllers can also -generate XML, JSON, PDFs, mobile-specific views, and more. Controllers manipulate models +generate XML, JSON, PDFs, mobile-specific views, and more. Controllers manipulate models and render view templates in order to generate the appropriate HTTP response. In Rails, the Controller and View layers are handled together by Action Pack. -These two layers are bundled in a single package due to their heavy interdependence. +These two layers are bundled in a single package due to their heavy interdependence. This is unlike the relationship between Active Record and Action Pack which are -independent. Each of these packages can be used independently outside of Rails. You +independent. Each of these packages can be used independently outside of Rails. You can read more about Action Pack in its {README}[link:/rails/rails/blob/master/actionpack/README.rdoc]. == Getting Started @@ -67,12 +67,11 @@ We encourage you to contribute to Ruby on Rails! Please check out the {Contribut guide}[http://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html] for guidelines about how to proceed. {Join us}[http://contributors.rubyonrails.org]! -== Build Status {<img src="https://secure.travis-ci.org/rails/rails.png"/>}[http://travis-ci.org/rails/rails] +== Code Status -== Dependency Status {<img src="https://gemnasium.com/rails/rails.png?travis"/>}[https://gemnasium.com/rails/rails] +* {<img src="https://secure.travis-ci.org/rails/rails.png"/>}[http://travis-ci.org/rails/rails] +* {<img src="https://gemnasium.com/rails/rails.png?travis"/>}[https://gemnasium.com/rails/rails] == License -Ruby on Rails is released under the MIT license: - -* http://www.opensource.org/licenses/MIT +Ruby on Rails is released under the {MIT License}[http://www.opensource.org/licenses/MIT]. @@ -75,10 +75,10 @@ RDoc::Task.new do |rdoc| rdoc_main.gsub!(/^(?=\S).*?\b(?=Rails)\b/) { "#$&\\" } rdoc_main.gsub!(%r{link:/rails/rails/blob/master/(\w+)/README\.rdoc}, "link:files/\\1/README_rdoc.html") - # Remove Travis and Gemnasium status images from API pages. Only GitHub - # README page gets these images. Travis' https build image is used to avoid - # GitHub caching: http://about.travis-ci.org/docs/user/status-images - rdoc_main.gsub!(%r{^== (Build|Dependency) Status.*}, '') + # Remove Travis and Gemnasium status images from API pages. Only the GitHub + # README page gets these images. Travis's HTTPS build image is used to + # avoid GitHub caching: http://about.travis-ci.org/docs/user/status-images + rdoc_main.gsub!(/^== Code Status(\n(?!==).*)*/, '') File.open(RDOC_MAIN, 'w') do |f| f.write(rdoc_main) diff --git a/actionmailer/CHANGELOG.md b/actionmailer/CHANGELOG.md index 68b34805ff..28a5c0ab71 100644 --- a/actionmailer/CHANGELOG.md +++ b/actionmailer/CHANGELOG.md @@ -1,9 +1,9 @@ ## Rails 4.0.0 (unreleased) ## -* Support `Mailer.deliver_foo(*args)` as a synonym for - `Mailer.foo(*args).deliver`. This makes it easy to write e.g. - `Mailer.expects(:deliver_foo)` when testing code that calls - the mailer. *Jon Leighton* +* Do not render views when mail() isn't called. + Fix #7761 + + *Yves Senn* * Allow delivery method options to be set per mail instance *Aditya Sanghi* diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index fee4a64248..a66e81f1c3 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -142,7 +142,6 @@ module ActionMailer # for delivery later: # # Notifier.welcome(david).deliver # sends the email - # Notifier.deliver_welcome(david) # synonym for the former # mail = Notifier.welcome(david) # => a Mail::Message object # mail.deliver # sends the email # @@ -488,8 +487,6 @@ module ActionMailer def method_missing(method_name, *args) if action_methods.include?(method_name.to_s) QueuedMessage.new(queue, self, method_name, *args) - elsif method_name.to_s =~ /^deliver_(.+)$/ && action_methods.include?($1) - public_send($1, *args).deliver else super end @@ -510,7 +507,19 @@ module ActionMailer def process(*args) #:nodoc: lookup_context.skip_default_locale! - super + + generated_mail = super + unless generated_mail + @_message = NullMail.new + end + end + + class NullMail #:nodoc: + def body; '' end + + def method_missing(*args) + nil + end end def mailer_name diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb index 1cb3ce63fe..b07b352082 100644 --- a/actionmailer/test/base_test.rb +++ b/actionmailer/test/base_test.rb @@ -499,6 +499,12 @@ class BaseTest < ActiveSupport::TestCase end end + test 'the view is not rendered when mail was never called' do + mail = BaseMailer.without_mail_call + assert_equal('', mail.body.to_s.strip) + mail.deliver + end + # Before and After hooks class MyObserver @@ -662,13 +668,6 @@ class BaseTest < ActiveSupport::TestCase assert_equal ["robert.pankowecki@gmail.com"], DefaultFromMailer.welcome.from end - test "Mailer.deliver_welcome calls Mailer.welcome.deliver" do - BaseMailer.deliveries.clear - BaseMailer.deliver_welcome(subject: 'omg') - assert_equal 1, BaseMailer.deliveries.length - assert_equal 'omg', BaseMailer.deliveries.first.subject - end - protected # Execute the block setting the given values and restoring old values after diff --git a/actionmailer/test/fixtures/base_mailer/without_mail_call.erb b/actionmailer/test/fixtures/base_mailer/without_mail_call.erb new file mode 100644 index 0000000000..290379d5fb --- /dev/null +++ b/actionmailer/test/fixtures/base_mailer/without_mail_call.erb @@ -0,0 +1 @@ +<% raise 'the template should not be rendered' %>
\ No newline at end of file diff --git a/actionmailer/test/mailers/base_mailer.rb b/actionmailer/test/mailers/base_mailer.rb index f25d9b9aff..52342bc59e 100644 --- a/actionmailer/test/mailers/base_mailer.rb +++ b/actionmailer/test/mailers/base_mailer.rb @@ -115,4 +115,7 @@ class BaseMailer < ActionMailer::Base def email_with_translations mail body: render("email_with_translations", formats: [:html]) end + + def without_mail_call + end end diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 48ba1518e0..0ffae5086e 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,5 +1,10 @@ ## Rails 4.0.0 (unreleased) ## +* `BestStandardsSupport` middleware now appends it's `X-UA-Compatible` value to app's + returned value if any. Fix #8086 + + *Nikita Afanasenko* + * `date_select` helper accepts `with_css_classes: true` to add css classes similar with type of generated select tags. @@ -134,16 +139,6 @@ *Tima Maslyuchenko* -* Fixed a bug with shorthand routes scoped with the `:module` option not - adding the module to the controller as described in issue #6497. - This should now work properly: - - scope :module => "engine" do - get "api/version" # routes to engine/api#version - end - - *Luiz Felipe Garcia Pereira* - * Sprockets integration has been extracted from Action Pack to the `sprockets-rails` gem. `rails` gem is depending on `sprockets-rails` by default. diff --git a/actionpack/lib/abstract_controller/base.rb b/actionpack/lib/abstract_controller/base.rb index 388e043f0b..56dc9ab7a1 100644 --- a/actionpack/lib/abstract_controller/base.rb +++ b/actionpack/lib/abstract_controller/base.rb @@ -34,6 +34,15 @@ module AbstractController @abstract = true end + def inherited(klass) # :nodoc: + # define the abstract ivar on subclasses so that we don't get + # uninitialized ivar warnings + unless klass.instance_variable_defined?(:@abstract) + klass.instance_variable_set(:@abstract, false) + end + super + end + # A list of all internal methods for a controller. This finds the first # abstract superclass of a controller, and gets a list of all public # instance methods on that abstract class. Public instance methods of @@ -42,6 +51,7 @@ module AbstractController # (ActionController::Metal and ActionController::Base are defined as abstract) def internal_methods controller = self + controller = controller.superclass until controller.abstract? controller.public_instance_methods(true) end diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb index 3da2834af0..4b984d0558 100644 --- a/actionpack/lib/abstract_controller/rendering.rb +++ b/actionpack/lib/abstract_controller/rendering.rb @@ -51,7 +51,7 @@ module AbstractController @view_context_class ||= begin routes = respond_to?(:_routes) && _routes helpers = respond_to?(:_helpers) && _helpers - + Class.new(ActionView::Base) do if routes include routes.url_helpers diff --git a/actionpack/lib/action_controller/metal/exceptions.rb b/actionpack/lib/action_controller/metal/exceptions.rb index 3c9d0c86a7..3844dbf2a6 100644 --- a/actionpack/lib/action_controller/metal/exceptions.rb +++ b/actionpack/lib/action_controller/metal/exceptions.rb @@ -3,6 +3,15 @@ module ActionController end class BadRequest < ActionControllerError #:nodoc: + attr_reader :original_exception + + def initialize(type = nil, e = nil) + return super() unless type && e + + super("Invalid #{type} parameters: #{e.message}") + @original_exception = e + set_backtrace e.backtrace + end end class RenderError < ActionControllerError #:nodoc: diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb index 73f2e94cd1..04dc1d37f7 100644 --- a/actionpack/lib/action_controller/metal/strong_parameters.rb +++ b/actionpack/lib/action_controller/metal/strong_parameters.rb @@ -196,7 +196,7 @@ module ActionController def permit(*filters) params = self.class.new - filters.each do |filter| + filters.flatten.each do |filter| case filter when Symbol, String then if has_key?(filter) diff --git a/actionpack/lib/action_controller/record_identifier.rb b/actionpack/lib/action_controller/record_identifier.rb index bffd2a02d0..b49128c184 100644 --- a/actionpack/lib/action_controller/record_identifier.rb +++ b/actionpack/lib/action_controller/record_identifier.rb @@ -8,12 +8,12 @@ module ActionController 'ActionView::RecodIdentifier module.' def dom_id(record, prefix = nil) - ActiveSupport::Deprecation.warn 'dom_id ' + MESSAGE + ActiveSupport::Deprecation.warn('dom_id ' + MESSAGE) ActionView::RecordIdentifier.dom_id(record, prefix) end def dom_class(record, prefix = nil) - ActiveSupport::Deprecation.warn 'dom_class ' + MESSAGE + ActiveSupport::Deprecation.warn('dom_class ' + MESSAGE) ActionView::RecordIdentifier.dom_class(record, prefix) end end diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb index d007133183..5aecb59df9 100644 --- a/actionpack/lib/action_controller/test_case.rb +++ b/actionpack/lib/action_controller/test_case.rb @@ -476,7 +476,7 @@ module ActionController def process(action, http_method = 'GET', *args) check_required_ivars - http_method, args = handle_old_process_api(http_method, args) + 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 @@ -579,10 +579,10 @@ module ActionController end end - def handle_old_process_api(http_method, args) + 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)") + 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 diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb index fc8825d6d9..3de927abc8 100644 --- a/actionpack/lib/action_dispatch/http/request.rb +++ b/actionpack/lib/action_dispatch/http/request.rb @@ -251,21 +251,17 @@ module ActionDispatch # Override Rack's GET method to support indifferent access def GET - begin - @env["action_dispatch.request.query_parameters"] ||= (normalize_parameters(super) || {}) - rescue TypeError => e - raise ActionController::BadRequest, "Invalid query parameters: #{e.message}" - end + @env["action_dispatch.request.query_parameters"] ||= (normalize_parameters(super) || {}) + rescue TypeError => e + raise ActionController::BadRequest.new(:query, e) end alias :query_parameters :GET # Override Rack's POST method to support indifferent access def POST - begin - @env["action_dispatch.request.request_parameters"] ||= (normalize_parameters(super) || {}) - rescue TypeError => e - raise ActionController::BadRequest, "Invalid request parameters: #{e.message}" - end + @env["action_dispatch.request.request_parameters"] ||= (normalize_parameters(super) || {}) + rescue TypeError => e + raise ActionController::BadRequest.new(:request, e) end alias :request_parameters :POST diff --git a/actionpack/lib/action_dispatch/middleware/best_standards_support.rb b/actionpack/lib/action_dispatch/middleware/best_standards_support.rb index 69adcc419f..d338996240 100644 --- a/actionpack/lib/action_dispatch/middleware/best_standards_support.rb +++ b/actionpack/lib/action_dispatch/middleware/best_standards_support.rb @@ -15,7 +15,13 @@ module ActionDispatch def call(env) status, headers, body = @app.call(env) - headers["X-UA-Compatible"] = @header + + if headers["X-UA-Compatible"] && @header + headers["X-UA-Compatible"] << "," << @header.to_s + else + headers["X-UA-Compatible"] = @header + end + [status, headers, body] end end diff --git a/actionpack/lib/action_dispatch/middleware/remote_ip.rb b/actionpack/lib/action_dispatch/middleware/remote_ip.rb index ec15a2a715..5abf8f2802 100644 --- a/actionpack/lib/action_dispatch/middleware/remote_ip.rb +++ b/actionpack/lib/action_dispatch/middleware/remote_ip.rb @@ -63,9 +63,9 @@ module ActionDispatch }x def initialize(env, middleware) - @env = env - @middleware = middleware - @calculated_ip = false + @env = env + @middleware = middleware + @ip = nil end # Determines originating IP address. REMOTE_ADDR is the standard @@ -100,9 +100,7 @@ module ActionDispatch end def to_s - return @ip if @calculated_ip - @calculated_ip = true - @ip = calculate_ip + @ip ||= calculate_ip end private diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index e337e30766..045299281c 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -182,7 +182,7 @@ module ActionDispatch controller ||= default_controller action ||= default_action - unless controller.is_a?(Regexp) + unless controller.is_a?(Regexp) || to_shorthand controller = [@scope[:module], controller].compact.join("/").presence end diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb index 2928263163..95cd89a166 100644 --- a/actionpack/lib/action_dispatch/testing/integration.rb +++ b/actionpack/lib/action_dispatch/testing/integration.rb @@ -3,6 +3,7 @@ require 'uri' require 'active_support/core_ext/kernel/singleton_class' require 'active_support/core_ext/object/try' require 'rack/test' +require 'minitest/unit' module ActionDispatch module Integration #:nodoc: @@ -497,8 +498,7 @@ module ActionDispatch 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", caller + ActiveSupport::Deprecation.warn "Rails application fallback is deprecated and no longer works, please set ActionDispatch.test_app" end @@app || ActionDispatch.test_app diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb index 77d8794b7d..459f95bb73 100644 --- a/actionpack/lib/action_view/helpers/date_helper.rb +++ b/actionpack/lib/action_view/helpers/date_helper.rb @@ -69,7 +69,7 @@ module ActionView 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", caller + "as a part of options hash, not a boolean argument" options[:include_seconds] ||= !!include_seconds_or_options end diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index b87c2e936f..6abf1e1751 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -1181,8 +1181,7 @@ module ActionView 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.") + ActiveSupport::Deprecation.warn "Giving a block to FormBuilder is deprecated and has no effect anymore." end @nested_child_index = {} diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb index a33b233ed5..b7b3db959e 100644 --- a/actionpack/lib/action_view/helpers/form_options_helper.rb +++ b/actionpack/lib/action_view/helpers/form_options_helper.rb @@ -519,7 +519,9 @@ module ActionView else prompt = options options = {} - ActiveSupport::Deprecation.warn "Passing the prompt to grouped_options_for_select as an argument is deprecated. Please use an options hash like `{ prompt: #{prompt.inspect} }`." + 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 body = "".html_safe diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb index 0257a13bc4..7f42d6e9c3 100644 --- a/actionpack/lib/action_view/helpers/form_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb @@ -425,13 +425,17 @@ module ActionView options = options.stringify_keys if disable_with = options.delete("disable_with") - ActiveSupport::Deprecation.warn ":disable_with option is deprecated and will be removed from Rails 4.1. Use ':data => { :disable_with => \'Text\' }' instead" + message = ":disable_with option is deprecated and will be removed from Rails 4.1. " \ + "Use 'data: { disable_with: \'Text\' }' instead." + ActiveSupport::Deprecation.warn message options["data-disable-with"] = disable_with end if confirm = options.delete("confirm") - ActiveSupport::Deprecation.warn ":confirm option is deprecated and will be removed from Rails 4.1. Use ':data => { :confirm => \'Text\' }' instead'" + message = ":confirm option is deprecated and will be removed from Rails 4.1. " \ + "Use 'data: { confirm: \'Text\' }' instead'." + ActiveSupport::Deprecation.warn message options["data-confirm"] = confirm end @@ -483,13 +487,17 @@ module ActionView options = options.stringify_keys if disable_with = options.delete("disable_with") - ActiveSupport::Deprecation.warn ":disable_with option is deprecated and will be removed from Rails 4.1. Use ':data => { :disable_with => \'Text\' }' instead" + message = ":disable_with option is deprecated and will be removed from Rails 4.1. " \ + "Use 'data: { disable_with: \'Text\' }' instead." + ActiveSupport::Deprecation.warn message options["data-disable-with"] = disable_with end if confirm = options.delete("confirm") - ActiveSupport::Deprecation.warn ":confirm option is deprecated and will be removed from Rails 4.1. Use ':data => { :confirm => \'Text\' }' instead'" + message = ":confirm option is deprecated and will be removed from Rails 4.1. " \ + "Use 'data: { confirm: \'Text\' }' instead'." + ActiveSupport::Deprecation.warn message options["data-confirm"] = confirm end @@ -533,7 +541,9 @@ module ActionView options = options.stringify_keys if confirm = options.delete("confirm") - ActiveSupport::Deprecation.warn ":confirm option is deprecated and will be removed from Rails 4.1. Use ':data => { :confirm => \'Text\' }' instead'" + message = ":confirm option is deprecated and will be removed from Rails 4.1. " \ + "Use 'data: { confirm: \'Text\' }' instead'." + ActiveSupport::Deprecation.warn message options["data-confirm"] = confirm end diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb index a30326349a..1a99fc7091 100644 --- a/actionpack/lib/action_view/helpers/javascript_helper.rb +++ b/actionpack/lib/action_view/helpers/javascript_helper.rb @@ -78,7 +78,8 @@ module ActionView # # => <input class="ok" onclick="alert('Hello world!');" type="button" value="Greeting" /> # def button_to_function(name, function=nil, html_options={}) - message = "button_to_function is deprecated and will be removed from Rails 4.1. Use Unobtrusive JavaScript instead." + message = "button_to_function is deprecated and will be removed from Rails 4.1. We recomend to use Unobtrusive JavaScript instead. " + + "See http://guides.rubyonrails.org/working_with_javascript_in_rails.html#unobtrusive-javascript" ActiveSupport::Deprecation.warn message onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function};" @@ -99,7 +100,8 @@ module ActionView # # => <a class="nav_link" href="#" onclick="alert('Hello world!'); return false;">Greeting</a> # def link_to_function(name, function, html_options={}) - message = "link_to_function is deprecated and will be removed from Rails 4.1. Use Unobtrusive JavaScript instead." + message = "link_to_function is deprecated and will be removed from Rails 4.1. We recomend to use Unobtrusive JavaScript instead. " + + "See http://guides.rubyonrails.org/working_with_javascript_in_rails.html#unobtrusive-javascript" ActiveSupport::Deprecation.warn message onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function}; return false;" diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb index 9bfda0ee9c..fd671c9c07 100644 --- a/actionpack/lib/action_view/helpers/url_helper.rb +++ b/actionpack/lib/action_view/helpers/url_helper.rb @@ -577,7 +577,9 @@ module ActionView method = html_options.delete('method') if confirm - ActiveSupport::Deprecation.warn ":confirm option is deprecated and will be removed from Rails 4.1. Use ':data => { :confirm => \'Text\' }' instead" + message = ":confirm option is deprecated and will be removed from Rails 4.1. " \ + "Use 'data: { confirm: \'Text\' }' instead." + ActiveSupport::Deprecation.warn message html_options["data-confirm"] = confirm end @@ -585,7 +587,9 @@ module ActionView add_method_to_attributes!(html_options, method) if method if disable_with - ActiveSupport::Deprecation.warn ":disable_with option is deprecated and will be removed from Rails 4.1. Use ':data => { :disable_with => \'Text\' }' instead" + message = ":disable_with option is deprecated and will be removed from Rails 4.1. " \ + "Use 'data: { disable_with: \'Text\' }' instead." + ActiveSupport::Deprecation.warn message html_options["data-disable-with"] = disable_with end diff --git a/actionpack/lib/action_view/renderer/template_renderer.rb b/actionpack/lib/action_view/renderer/template_renderer.rb index 156ad4e547..2a5ea5a711 100644 --- a/actionpack/lib/action_view/renderer/template_renderer.rb +++ b/actionpack/lib/action_view/renderer/template_renderer.rb @@ -29,8 +29,11 @@ module ActionView handler = Template.handler_for_extension(options[:type] || "erb") Template.new(options[:inline], "inline template", handler, :locals => keys) elsif options.key?(:template) - options[:template].respond_to?(:render) ? - options[:template] : find_template(options[:template], options[:prefixes], false, keys, @details) + if options[:template].respond_to?(:render) + options[:template] + else + find_template(options[:template], options[:prefixes], false, keys, @details) + end else raise ArgumentError, "You invoked render but did not give any of :partial, :template, :inline, :file or :text option." end diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb index 379cdc8a25..aefc42be48 100644 --- a/actionpack/lib/action_view/template.rb +++ b/actionpack/lib/action_view/template.rb @@ -149,7 +149,8 @@ module ActionView end def mime_type - ActiveSupport::Deprecation.warn 'Template#mime_type is deprecated and will be removed in Rails 4.1. Please use type method instead.' + message = 'Template#mime_type is deprecated and will be removed in Rails 4.1. Please use type method instead.' + ActiveSupport::Deprecation.warn message @mime_type ||= Mime::Type.lookup_by_extension(@formats.first.to_s) if @formats.first end diff --git a/actionpack/lib/action_view/template/resolver.rb b/actionpack/lib/action_view/template/resolver.rb index 25c6fd4aa8..fc77c1485d 100644 --- a/actionpack/lib/action_view/template/resolver.rb +++ b/actionpack/lib/action_view/template/resolver.rb @@ -232,8 +232,14 @@ module ActionView def extract_handler_and_format(path, default_formats) pieces = File.basename(path).split(".") pieces.shift + extension = pieces.pop - ActiveSupport::Deprecation.warn "The file #{path} did not specify a template handler. The default is currently ERB, but will change to RAW in the future." unless extension + unless extension + message = "The file #{path} did not specify a template handler. The default is currently ERB, " \ + "but will change to RAW in the future." + ActiveSupport::Deprecation.warn message + end + handler = Template.handler_for_extension(extension) format = pieces.last && Template::Types[pieces.last] [handler, format] diff --git a/actionpack/test/activerecord/controller_runtime_test.rb b/actionpack/test/activerecord/controller_runtime_test.rb index 2d789395ce..1df826fe2b 100644 --- a/actionpack/test/activerecord/controller_runtime_test.rb +++ b/actionpack/test/activerecord/controller_runtime_test.rb @@ -15,7 +15,7 @@ class ControllerRuntimeLogSubscriberTest < ActionController::TestCase def zero render :inline => "Zero DB runtime" end - + def redirect Project.all redirect_to :action => 'show' @@ -63,18 +63,18 @@ class ControllerRuntimeLogSubscriberTest < ActionController::TestCase assert_equal 2, @logger.logged(:info).size assert_match(/\(Views: [\d.]+ms \| ActiveRecord: 0.0ms\)/, @logger.logged(:info)[1]) end - + def test_log_with_active_record_when_redirecting get :redirect wait assert_equal 3, @logger.logged(:info).size assert_match(/\(ActiveRecord: [\d.]+ms\)/, @logger.logged(:info)[2]) end - + def test_include_time_query_time_after_rendering get :db_after_render wait - + assert_equal 2, @logger.logged(:info).size assert_match(/\(Views: [\d.]+ms \| ActiveRecord: ([1-9][\d.]+)ms\)/, @logger.logged(:info)[1]) end diff --git a/actionpack/test/controller/default_url_options_with_filter_test.rb b/actionpack/test/controller/default_url_options_with_filter_test.rb index ef028e8cdb..9a9ab17fee 100644 --- a/actionpack/test/controller/default_url_options_with_filter_test.rb +++ b/actionpack/test/controller/default_url_options_with_filter_test.rb @@ -16,7 +16,7 @@ class ControllerWithBeforeFilterAndDefaultUrlOptions < ActionController::Base def default_url_options {:locale => "de"} - end + end end class ControllerWithBeforeFilterAndDefaultUrlOptionsTest < ActionController::TestCase diff --git a/actionpack/test/controller/http_basic_authentication_test.rb b/actionpack/test/controller/http_basic_authentication_test.rb index 7286b249c7..2dcfda02a7 100644 --- a/actionpack/test/controller/http_basic_authentication_test.rb +++ b/actionpack/test/controller/http_basic_authentication_test.rb @@ -19,7 +19,7 @@ class HttpBasicAuthenticationTest < ActionController::TestCase def show render :text => 'Only for loooooong credentials' end - + def search render :text => 'All inline' end @@ -118,7 +118,7 @@ class HttpBasicAuthenticationTest < ActionController::TestCase assert assigns(:logged_in) assert_equal 'Definitely Maybe', @response.body end - + test "authenticate with class method" do @request.env['HTTP_AUTHORIZATION'] = encode_credentials('David', 'Goliath') get :search diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb index b21d35c846..d183b0be17 100644 --- a/actionpack/test/controller/mime_responds_test.rb +++ b/actionpack/test/controller/mime_responds_test.rb @@ -240,7 +240,7 @@ class RespondToControllerTest < ActionController::TestCase assert_equal 'HTML', @response.body @request.accept = "text/javascript, text/html" - + assert_raises(ActionController::UnknownFormat) do xhr :get, :just_xml end diff --git a/actionpack/test/controller/new_base/middleware_test.rb b/actionpack/test/controller/new_base/middleware_test.rb index ccef060863..6b7b5e10e3 100644 --- a/actionpack/test/controller/new_base/middleware_test.rb +++ b/actionpack/test/controller/new_base/middleware_test.rb @@ -25,14 +25,14 @@ module MiddlewareTest result end end - + class BlockMiddleware attr_accessor :configurable_message def initialize(app, &block) @app = app yield(self) if block_given? end - + def call(env) result = @app.call(env) result[1]["Configurable-Message"] = configurable_message diff --git a/actionpack/test/controller/new_base/render_partial_test.rb b/actionpack/test/controller/new_base/render_partial_test.rb index b4a25c49c9..2f1aa22208 100644 --- a/actionpack/test/controller/new_base/render_partial_test.rb +++ b/actionpack/test/controller/new_base/render_partial_test.rb @@ -28,7 +28,7 @@ module RenderPartial @test_unchanged = 'hello' end end - + class ChildController < BasicController; end class TestPartial < Rack::TestCase diff --git a/actionpack/test/controller/parameters/parameters_permit_test.rb b/actionpack/test/controller/parameters/parameters_permit_test.rb index ad970f0a9a..fc63470174 100644 --- a/actionpack/test/controller/parameters/parameters_permit_test.rb +++ b/actionpack/test/controller/parameters/parameters_permit_test.rb @@ -77,4 +77,8 @@ class ParametersPermitTest < ActiveSupport::TestCase ActionController::Parameters.permit_all_parameters = false end end + + test "permitting parameters as an array" do + assert_equal "32", @params[:person].permit([ :age ])[:age] + end end diff --git a/actionpack/test/controller/send_file_test.rb b/actionpack/test/controller/send_file_test.rb index 97ede35317..8bf3096888 100644 --- a/actionpack/test/controller/send_file_test.rb +++ b/actionpack/test/controller/send_file_test.rb @@ -134,7 +134,7 @@ class SendFileTest < ActionController::TestCase @controller.headers = {} assert_raise(ArgumentError){ @controller.send(:send_file_headers!, options) } end - + def test_send_file_headers_guess_type_from_extension { 'image.png' => 'image/png', diff --git a/actionpack/test/dispatch/best_standards_support_test.rb b/actionpack/test/dispatch/best_standards_support_test.rb new file mode 100644 index 0000000000..0737c40a39 --- /dev/null +++ b/actionpack/test/dispatch/best_standards_support_test.rb @@ -0,0 +1,34 @@ +require 'abstract_unit' + +class BestStandardsSupportTest < ActiveSupport::TestCase + def test_with_best_standards_support + _, headers, _ = app(true, {}).call({}) + assert_equal "IE=Edge,chrome=1", headers["X-UA-Compatible"] + end + + def test_with_builtin_best_standards_support + _, headers, _ = app(:builtin, {}).call({}) + assert_equal "IE=Edge", headers["X-UA-Compatible"] + end + + def test_without_best_standards_support + _, headers, _ = app(false, {}).call({}) + assert_equal nil, headers["X-UA-Compatible"] + end + + def test_appends_to_app_headers + app_headers = { "X-UA-Compatible" => "requiresActiveX=true" } + _, headers, _ = app(true, app_headers).call({}) + + expects = "requiresActiveX=true,IE=Edge,chrome=1" + assert_equal expects, headers["X-UA-Compatible"] + end + + private + + def app(type, headers) + app = proc { [200, headers, "response"] } + ActionDispatch::BestStandardsSupport.new(app, type) + end + +end diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb index a2b9571660..e2964f9071 100644 --- a/actionpack/test/dispatch/request_test.rb +++ b/actionpack/test/dispatch/request_test.rb @@ -550,15 +550,30 @@ class RequestTest < ActiveSupport::TestCase test "parameters still accessible after rack parse error" do mock_rack_env = { "QUERY_STRING" => "x[y]=1&x[y][][w]=2", "rack.input" => "foo" } request = nil - begin - request = stub_request(mock_rack_env) - request.parameters - rescue ActionController::BadRequest + request = stub_request(mock_rack_env) + + assert_raises(ActionController::BadRequest) do # rack will raise a TypeError when parsing this query string + request.parameters end + assert_equal({}, request.parameters) end + test "we have access to the original exception" do + mock_rack_env = { "QUERY_STRING" => "x[y]=1&x[y][][w]=2", "rack.input" => "foo" } + request = nil + request = stub_request(mock_rack_env) + + e = assert_raises(ActionController::BadRequest) do + # rack will raise a TypeError when parsing this query string + request.parameters + end + + assert e.original_exception + assert_equal e.original_exception.backtrace, e.backtrace + end + test "formats with accept header" do request = stub_request 'HTTP_ACCEPT' => 'text/html' request.expects(:parameters).at_least_once.returns({}) diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 93d89f7568..34606512dc 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -281,6 +281,8 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest scope(':version', :version => /.+/) do resources :users, :id => /.+?/, :format => /json|xml/ end + + get "products/list" end get 'sprockets.js' => ::TestRoutingMapper::SprocketsApp @@ -363,7 +365,6 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest resources :errors, :shallow => true do resources :notices end - get 'api/version' end scope :path => 'api' do @@ -1301,10 +1302,10 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest assert_equal 'account#shorthand', @response.body end - def test_match_shorthand_with_module - assert_equal '/api/version', api_version_path - get '/api/version' - assert_equal 'api/api#version', @response.body + def test_match_shorthand_inside_namespace_with_controller + assert_equal '/api/products/list', api_products_list_path + get '/api/products/list' + assert_equal 'api/products#list', @response.body end def test_dynamically_generated_helpers_on_collection_do_not_clobber_resources_url_helper diff --git a/actionpack/test/template/digestor_test.rb b/actionpack/test/template/digestor_test.rb index b9d26da3af..f493c8201d 100644 --- a/actionpack/test/template/digestor_test.rb +++ b/actionpack/test/template/digestor_test.rb @@ -63,7 +63,7 @@ class TemplateDigestorTest < ActionView::TestCase change_template("comments/_comment") end end - + def test_directory_depth_dependency assert_digest_difference("level/below/index") do change_template("level/below/_header") diff --git a/actionpack/test/template/form_tag_helper_test.rb b/actionpack/test/template/form_tag_helper_test.rb index 945dff2a29..0a94fa079b 100644 --- a/actionpack/test/template/form_tag_helper_test.rb +++ b/actionpack/test/template/form_tag_helper_test.rb @@ -411,7 +411,7 @@ class FormTagHelperTest < ActionView::TestCase end def test_submit_tag_with_deprecated_confirmation - assert_deprecated ":confirm option is deprecated and will be removed from Rails 4.1. Use ':data => { :confirm => \'Text\' }' instead" do + assert_deprecated ":confirm option is deprecated and will be removed from Rails 4.1. Use 'data: { confirm: \'Text\' }' instead" do assert_dom_equal( %(<input name='commit' type='submit' value='Save' data-confirm="Are you sure?" />), submit_tag("Save", :confirm => "Are you sure?") @@ -478,7 +478,7 @@ class FormTagHelperTest < ActionView::TestCase end def test_button_tag_with_deprecated_confirmation - assert_deprecated ":confirm option is deprecated and will be removed from Rails 4.1. Use ':data => { :confirm => \'Text\' }' instead" do + assert_deprecated ":confirm option is deprecated and will be removed from Rails 4.1. Use 'data: { confirm: \'Text\' }' instead" do assert_dom_equal( %(<button name="button" type="submit" data-confirm="Are you sure?">Save</button>), button_tag("Save", :type => "submit", :confirm => "Are you sure?") @@ -494,7 +494,7 @@ class FormTagHelperTest < ActionView::TestCase end def test_image_submit_tag_with_deprecated_confirmation - assert_deprecated ":confirm option is deprecated and will be removed from Rails 4.1. Use ':data => { :confirm => \'Text\' }' instead" do + assert_deprecated ":confirm option is deprecated and will be removed from Rails 4.1. Use 'data: { confirm: \'Text\' }' instead" do assert_dom_equal( %(<input type="image" src="/images/save.gif" data-confirm="Are you sure?" />), image_submit_tag("save.gif", :confirm => "Are you sure?") diff --git a/actionpack/test/template/javascript_helper_test.rb b/actionpack/test/template/javascript_helper_test.rb index 58784c26fa..56919dc592 100644 --- a/actionpack/test/template/javascript_helper_test.rb +++ b/actionpack/test/template/javascript_helper_test.rb @@ -43,42 +43,42 @@ class JavaScriptHelperTest < ActionView::TestCase end def test_button_to_function - assert_deprecated "button_to_function is deprecated and will be removed from Rails 4.1. Use Unobtrusive JavaScript instead." do + assert_deprecated do assert_dom_equal %(<input type="button" onclick="alert('Hello world!');" value="Greeting" />), button_to_function("Greeting", "alert('Hello world!')") end end def test_button_to_function_with_onclick - assert_deprecated "button_to_function is deprecated and will be removed from Rails 4.1. Use Unobtrusive JavaScript instead." do + assert_deprecated do assert_dom_equal "<input onclick=\"alert('Goodbye World :('); alert('Hello world!');\" type=\"button\" value=\"Greeting\" />", button_to_function("Greeting", "alert('Hello world!')", :onclick => "alert('Goodbye World :(')") end end def test_button_to_function_without_function - assert_deprecated "button_to_function is deprecated and will be removed from Rails 4.1. Use Unobtrusive JavaScript instead." do + assert_deprecated do assert_dom_equal "<input onclick=\";\" type=\"button\" value=\"Greeting\" />", button_to_function("Greeting") end end def test_link_to_function - assert_deprecated "link_to_function is deprecated and will be removed from Rails 4.1. Use Unobtrusive JavaScript instead." do + assert_deprecated do assert_dom_equal %(<a href="#" onclick="alert('Hello world!'); return false;">Greeting</a>), link_to_function("Greeting", "alert('Hello world!')") end end def test_link_to_function_with_existing_onclick - assert_deprecated "link_to_function is deprecated and will be removed from Rails 4.1. Use Unobtrusive JavaScript instead." do + assert_deprecated do assert_dom_equal %(<a href="#" onclick="confirm('Sanity!'); alert('Hello world!'); return false;">Greeting</a>), link_to_function("Greeting", "alert('Hello world!')", :onclick => "confirm('Sanity!')") end end def test_function_with_href - assert_deprecated "link_to_function is deprecated and will be removed from Rails 4.1. Use Unobtrusive JavaScript instead." do + assert_deprecated do assert_dom_equal %(<a href="http://example.com/" onclick="alert('Hello world!'); return false;">Greeting</a>), link_to_function("Greeting", "alert('Hello world!')", :href => 'http://example.com/') end diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb index ddf5c6a1b3..4e6a676fc6 100644 --- a/actionpack/test/template/render_test.rb +++ b/actionpack/test/template/render_test.rb @@ -451,12 +451,12 @@ module RenderTestCases assert_equal %(<title>David</title>), @view.render(:file => "test/layout_render_object") end - + def test_render_with_passing_couple_extensions_to_one_register_template_handler_function_call ActionView::Template.register_template_handler :foo1, :foo2, CustomHandler assert_equal @view.render(:inline => "Hello, World!", :type => :foo1), @view.render(:inline => "Hello, World!", :type => :foo2) end - + def test_render_throws_exception_when_no_extensions_passed_to_register_template_handler_function_call assert_raises(ArgumentError) { ActionView::Template.register_template_handler CustomHandler } end diff --git a/actionpack/test/template/tag_helper_test.rb b/actionpack/test/template/tag_helper_test.rb index f0a7ce0bc9..9e711c6529 100644 --- a/actionpack/test/template/tag_helper_test.rb +++ b/actionpack/test/template/tag_helper_test.rb @@ -87,6 +87,11 @@ class TagHelperTest < ActionView::TestCase assert_equal "<p class=\"song play>\">limelight</p>", str end + def test_content_tag_with_data_attributes + assert_dom_equal '<p data-number="1" data-string="hello" data-string-with-quotes="double"quote"party"">limelight</p>', + content_tag('p', "limelight", data: { number: 1, string: 'hello', string_with_quotes: 'double"quote"party"' }) + end + def test_cdata_section assert_equal "<![CDATA[<hello world>]]>", cdata_section("<hello world>") end @@ -118,8 +123,8 @@ class TagHelperTest < ActionView::TestCase def test_data_attributes ['data', :data].each { |data| - assert_dom_equal '<a data-a-float="3.14" data-a-big-decimal="-123.456" data-a-number="1" data-array="[1,2,3]" data-hash="{"key":"value"}" data-string="hello" data-symbol="foo" />', - tag('a', { data => { :a_float => 3.14, :a_big_decimal => BigDecimal.new("-123.456"), :a_number => 1, :string => 'hello', :symbol => :foo, :array => [1, 2, 3], :hash => { :key => 'value'} } }) + assert_dom_equal '<a data-a-float="3.14" data-a-big-decimal="-123.456" data-a-number="1" data-array="[1,2,3]" data-hash="{"key":"value"}" data-string-with-quotes="double"quote"party"" data-string="hello" data-symbol="foo" />', + tag('a', { data => { a_float: 3.14, a_big_decimal: BigDecimal.new("-123.456"), a_number: 1, string: 'hello', symbol: :foo, array: [1, 2, 3], hash: { key: 'value'}, string_with_quotes: 'double"quote"party"' } }) } end end diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb index bbfdf7f944..1bb625213d 100644 --- a/actionpack/test/template/url_helper_test.rb +++ b/actionpack/test/template/url_helper_test.rb @@ -95,7 +95,7 @@ class UrlHelperTest < ActiveSupport::TestCase end def test_button_to_with_deprecated_confirm - assert_deprecated ":confirm option is deprecated and will be removed from Rails 4.1. Use ':data => { :confirm => \'Text\' }' instead" do + assert_deprecated ":confirm option is deprecated and will be removed from Rails 4.1. Use 'data: { confirm: \'Text\' }' instead" do assert_dom_equal( %{<form method="post" action="http://www.example.com" class="button_to"><div><input data-confirm="Are you sure?" type="submit" value="Hello" /></div></form>}, button_to("Hello", "http://www.example.com", confirm: "Are you sure?") @@ -111,7 +111,7 @@ class UrlHelperTest < ActiveSupport::TestCase end def test_button_to_with_javascript_deprecated_disable_with - assert_deprecated ":disable_with option is deprecated and will be removed from Rails 4.1. Use ':data => { :disable_with => \'Text\' }' instead" do + assert_deprecated ":disable_with option is deprecated and will be removed from Rails 4.1. Use 'data: { disable_with: \'Text\' }' instead" do assert_dom_equal( %{<form method="post" action="http://www.example.com" class="button_to"><div><input data-disable-with="Greeting..." type="submit" value="Hello" /></div></form>}, button_to("Hello", "http://www.example.com", disable_with: "Greeting...") @@ -134,7 +134,7 @@ class UrlHelperTest < ActiveSupport::TestCase end def test_button_to_with_remote_and_javascript_with_deprecated_confirm - assert_deprecated ":confirm option is deprecated and will be removed from Rails 4.1. Use ':data => { :confirm => \'Text\' }' instead" do + assert_deprecated ":confirm option is deprecated and will be removed from Rails 4.1. Use 'data: { confirm: \'Text\' }' instead" do assert_dom_equal( %{<form method="post" action="http://www.example.com" class="button_to" data-remote="true"><div><input data-confirm="Are you sure?" type="submit" value="Hello" /></div></form>}, button_to("Hello", "http://www.example.com", remote: true, confirm: "Are you sure?") @@ -150,7 +150,7 @@ class UrlHelperTest < ActiveSupport::TestCase end def test_button_to_with_remote_and_javascript_deprecated_disable_with - assert_deprecated ":disable_with option is deprecated and will be removed from Rails 4.1. Use ':data => { :disable_with => \'Text\' }' instead" do + assert_deprecated ":disable_with option is deprecated and will be removed from Rails 4.1. Use 'data: { disable_with: \'Text\' }' instead" do assert_dom_equal( %{<form method="post" action="http://www.example.com" class="button_to" data-remote="true"><div><input data-disable-with="Greeting..." type="submit" value="Hello" /></div></form>}, button_to("Hello", "http://www.example.com", remote: true, disable_with: "Greeting...") @@ -267,19 +267,19 @@ class UrlHelperTest < ActiveSupport::TestCase end def test_link_tag_with_deprecated_confirm - assert_deprecated ":confirm option is deprecated and will be removed from Rails 4.1. Use ':data => { :confirm => \'Text\' }' instead" do + assert_deprecated ":confirm option is deprecated and will be removed from Rails 4.1. Use 'data: { confirm: \'Text\' }' instead" do assert_dom_equal( %{<a href="http://www.example.com" data-confirm="Are you sure?">Hello</a>}, link_to("Hello", "http://www.example.com", confirm: "Are you sure?") ) end - assert_deprecated ":confirm option is deprecated and will be removed from Rails 4.1. Use ':data => { :confirm => \'Text\' }' instead" do + assert_deprecated ":confirm option is deprecated and will be removed from Rails 4.1. Use 'data: { confirm: \'Text\' }' instead" do assert_dom_equal( %{<a href="http://www.example.com" data-confirm="You cant possibly be sure, can you?">Hello</a>}, link_to("Hello", "http://www.example.com", confirm: "You cant possibly be sure, can you?") ) end - assert_deprecated ":confirm option is deprecated and will be removed from Rails 4.1. Use ':data => { :confirm => \'Text\' }' instead" do + assert_deprecated ":confirm option is deprecated and will be removed from Rails 4.1. Use 'data: { confirm: \'Text\' }' instead" do assert_dom_equal( %{<a href="http://www.example.com" data-confirm="You cant possibly be sure,\n can you?">Hello</a>}, link_to("Hello", "http://www.example.com", confirm: "You cant possibly be sure,\n can you?") @@ -351,7 +351,7 @@ class UrlHelperTest < ActiveSupport::TestCase end def test_link_tag_using_post_javascript_and_with_deprecated_confirm - assert_deprecated ":confirm option is deprecated and will be removed from Rails 4.1. Use ':data => { :confirm => \'Text\' }' instead" do + assert_deprecated ":confirm option is deprecated and will be removed from Rails 4.1. Use 'data: { confirm: \'Text\' }' instead" do assert_dom_equal( %{<a href="http://www.example.com" data-method="post" rel="nofollow" data-confirm="Are you serious?">Hello</a>}, link_to("Hello", "http://www.example.com", method: :post, confirm: "Are you serious?") @@ -367,7 +367,7 @@ class UrlHelperTest < ActiveSupport::TestCase end def test_link_tag_using_delete_javascript_and_href_and_with_deprecated_confirm - assert_deprecated ":confirm option is deprecated and will be removed from Rails 4.1. Use ':data => { :confirm => \'Text\' }' instead" do + assert_deprecated ":confirm option is deprecated and will be removed from Rails 4.1. Use 'data: { confirm: \'Text\' }' instead" do assert_dom_equal( %{<a href="\#" rel="nofollow" data-confirm="Are you serious?" data-method="delete">Destroy</a>}, link_to("Destroy", "http://www.example.com", method: :delete, href: '#', confirm: "Are you serious?") diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb index a1d01b2c89..86eaad830e 100644 --- a/activemodel/lib/active_model/attribute_methods.rb +++ b/activemodel/lib/active_model/attribute_methods.rb @@ -383,12 +383,11 @@ module ActiveModel def initialize(options = {}) if options[:prefix] == '' || options[:suffix] == '' - ActiveSupport::Deprecation.warn( - "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." - ) + 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, '') diff --git a/activemodel/lib/active_model/observing.rb b/activemodel/lib/active_model/observing.rb index 9419645e7b..5f1c99ce62 100644 --- a/activemodel/lib/active_model/observing.rb +++ b/activemodel/lib/active_model/observing.rb @@ -172,7 +172,7 @@ module ActiveModel # <tt>count_observers</tt> is deprecated. Use #observers_count. def count_observers msg = "count_observers is deprecated in favor of observers_count" - ActiveSupport::Deprecation.warn(msg) + ActiveSupport::Deprecation.warn msg observers_count end diff --git a/activemodel/lib/active_model/serializers/json.rb b/activemodel/lib/active_model/serializers/json.rb index a4252b995d..9d984b7a18 100644 --- a/activemodel/lib/active_model/serializers/json.rb +++ b/activemodel/lib/active_model/serializers/json.rb @@ -1,8 +1,8 @@ require 'active_support/json' module ActiveModel - # == Active Model JSON Serializer module Serializers + # == Active Model JSON Serializer module JSON extend ActiveSupport::Concern include ActiveModel::Serialization diff --git a/activemodel/lib/active_model/serializers/xml.rb b/activemodel/lib/active_model/serializers/xml.rb index cf742d0569..fb6093cce5 100755 --- a/activemodel/lib/active_model/serializers/xml.rb +++ b/activemodel/lib/active_model/serializers/xml.rb @@ -4,12 +4,16 @@ require 'active_support/core_ext/hash/conversions' require 'active_support/core_ext/hash/slice' module ActiveModel - # == Active Model XML Serializer module Serializers + # == Active Model XML Serializer module Xml extend ActiveSupport::Concern include ActiveModel::Serialization + included do + extend ActiveModel::Naming + end + class Serializer #:nodoc: class Attribute #:nodoc: attr_reader :name, :value, :type diff --git a/activemodel/test/cases/serializers/json_serialization_test.rb b/activemodel/test/cases/serializers/json_serialization_test.rb index e2690f1827..fd4d068354 100644 --- a/activemodel/test/cases/serializers/json_serialization_test.rb +++ b/activemodel/test/cases/serializers/json_serialization_test.rb @@ -4,7 +4,6 @@ require 'models/automobile' require 'active_support/core_ext/object/instance_variables' class Contact - extend ActiveModel::Naming include ActiveModel::Serializers::JSON include ActiveModel::Validations @@ -156,6 +155,15 @@ class JsonSerializationTest < ActiveModel::TestCase end end + test "as_json should keep the default order in the hash" do + json = @contact.as_json + keys = json.keys + + %w(name age created_at awesome preferences).each_with_index do |field, index| + assert_equal keys.index(field), index + end + end + test "from_json should work without a root (class attribute)" do json = @contact.to_json result = Contact.new.from_json(json) diff --git a/activemodel/test/cases/serializers/xml_serialization_test.rb b/activemodel/test/cases/serializers/xml_serialization_test.rb index e2bb0dda0b..90ddf8ff0c 100755 --- a/activemodel/test/cases/serializers/xml_serialization_test.rb +++ b/activemodel/test/cases/serializers/xml_serialization_test.rb @@ -4,7 +4,6 @@ require 'active_support/core_ext/object/instance_variables' require 'ostruct' class Contact - extend ActiveModel::Naming include ActiveModel::Serializers::Xml attr_accessor :address, :friends @@ -25,7 +24,6 @@ class Customer < Struct.new(:name) end class Address - extend ActiveModel::Naming include ActiveModel::Serializers::Xml attr_accessor :street, :city, :state, :zip, :apt_number diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index ffd19a5334..1d43840573 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,5 +1,63 @@ ## Rails 4.0.0 (unreleased) ## +* Fix `find_in_batches` crashing when IDs are strings and start option is not specified. + + *Alexis Bernard* + +* `AR::Base#attributes_before_type_cast` now returns unserialized values for serialized attributes. + + *Nikita Afanasenko* + +* Use query cache/uncache when using DATABASE_URL. + Fix #6951. + + *kennyj* + +* Added `#none!` method for mutating `ActiveRecord::Relation` objects to a NullRelation. + It acts like `#none` but modifies relation in place. + + *Juanjo Bazán* + +* Fix bug where `update_columns` and `update_column` would not let you update the primary key column. + + *Henrik Nyh* + +* The `create_table` method raises an `ArgumentError` when the primary key column is redefined. + Fix #6378 + + *Yves Senn* + +* `ActiveRecord::AttributeMethods#[]` raises `ActiveModel::MissingAttributeError` + error if the given attribute is missing. Fixes #5433. + + class Person < ActiveRecord::Base + belongs_to :company + end + + # Before: + person = Person.select('id').first + person[:name] # => nil + person.name # => ActiveModel::MissingAttributeError: missing_attribute: name + person[:company_id] # => nil + person.company # => nil + + # After: + person = Person.select('id').first + person[:name] # => ActiveModel::MissingAttributeError: missing_attribute: name + person.name # => ActiveModel::MissingAttributeError: missing_attribute: name + person[:company_id] # => ActiveModel::MissingAttributeError: missing_attribute: company_id + person.company # => ActiveModel::MissingAttributeError: missing_attribute: company_id + + *Francesco Rodriguez* + +* Small binary fields use the `VARBINARY` MySQL type, instead of `TINYBLOB`. + + *Victor Costan* + +* Decode URI encoded attributes on database connection URLs. + + *Shawn Veader* + * Add `find_or_create_by`, `find_or_create_by!` and `find_or_initialize_by` methods to `Relation`. @@ -38,12 +96,12 @@ *Jon Leighton* -* Fix bug with presence validation of associations. Would incorrectly add duplicated errors +* Fix bug with presence validation of associations. Would incorrectly add duplicated errors when the association was blank. Bug introduced in 1fab518c6a75dac5773654646eb724a59741bc13. *Scott Willson* -* Fix bug where sum(expression) returns string '0' for no matching records +* Fix bug where sum(expression) returns string '0' for no matching records. Fixes #7439 *Tim Macfarlane* @@ -338,7 +396,7 @@ * Fix AR#dup to nullify the validation errors in the dup'ed object. Previously the original and the dup'ed object shared the same errors. - * Christian Seiler* + *Christian Seiler* * Raise `ArgumentError` if list of attributes to change is empty in `update_all`. @@ -926,34 +984,6 @@ *Aaron Patterson* -* Added the `ActiveRecord::Model` module which can be included in a - class as an alternative to inheriting from `ActiveRecord::Base`: - - class Post - include ActiveRecord::Model - end - - Please note: - - * Up until now it has been safe to assume that all AR models are - descendants of `ActiveRecord::Base`. This is no longer a safe - assumption, but it may transpire that there are areas of the - code which still make this assumption. So there may be - 'teething difficulties' with this feature. (But please do try it - and report bugs.) - - * Plugins & libraries etc that add methods to `ActiveRecord::Base` - will not be compatible with `ActiveRecord::Model`. Those libraries - should add to `ActiveRecord::Model` instead (which is included in - `Base`), or better still, avoid monkey-patching AR and instead - provide a module that users can include where they need it. - - * To minimise the risk of conflicts with other code, it is - advisable to include `ActiveRecord::Model` early in your class - definition. - - *Jon Leighton* - * PostgreSQL hstore records can be created. *Aaron Patterson* diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index 1675127ab0..45122539f1 100644 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -43,7 +43,6 @@ module ActiveRecord autoload :Integration autoload :Migration autoload :Migrator, 'active_record/migration' - autoload :Model autoload :ModelSchema autoload :NestedAttributes autoload :Observer diff --git a/activerecord/lib/active_record/associations/alias_tracker.rb b/activerecord/lib/active_record/associations/alias_tracker.rb index 84540a7000..0c23029981 100644 --- a/activerecord/lib/active_record/associations/alias_tracker.rb +++ b/activerecord/lib/active_record/associations/alias_tracker.rb @@ -8,7 +8,7 @@ module ActiveRecord attr_reader :aliases, :table_joins, :connection # table_joins is an array of arel joins which might conflict with the aliases we assign here - def initialize(connection = ActiveRecord::Model.connection, table_joins = []) + def initialize(connection = Base.connection, table_joins = []) @aliases = Hash.new { |h,k| h[k] = initial_count_for(k) } @table_joins = table_joins @connection = connection diff --git a/activerecord/lib/active_record/associations/association.rb b/activerecord/lib/active_record/associations/association.rb index 2a5f727728..99e7383d42 100644 --- a/activerecord/lib/active_record/associations/association.rb +++ b/activerecord/lib/active_record/associations/association.rb @@ -85,7 +85,7 @@ module ActiveRecord end def scoped - ActiveSupport::Deprecation.warn("#scoped is deprecated. use #scope instead.") + ActiveSupport::Deprecation.warn "#scoped is deprecated. use #scope instead." scope end diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb index 7f39d3083e..54215cf88d 100644 --- a/activerecord/lib/active_record/associations/collection_association.rb +++ b/activerecord/lib/active_record/associations/collection_association.rb @@ -576,7 +576,9 @@ module ActiveRecord args.shift if args.first.is_a?(Hash) && args.first.empty? collection = fetch_first_or_last_using_find?(args) ? scope : load_target - collection.send(type, *args).tap {|it| set_inverse_instance it } + collection.send(type, *args).tap do |record| + set_inverse_instance record if record.is_a? ActiveRecord::Base + end end end end diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index 1dd9a3d45d..437fd00948 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -59,7 +59,7 @@ module ActiveRecord raise DangerousAttributeError, "#{method_name} is defined by ActiveRecord" end - if [Base, Model].include?(active_record_super) + if superclass == Base super else # If B < A and A defines its own attribute method, then we don't want to overwrite that. @@ -269,17 +269,24 @@ module ActiveRecord end # Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example, - # "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)). - # (Alias for <tt>read_attribute</tt>). + # "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)). It raises + # <tt>ActiveModel::MissingAttributeError</tt> if the identified attribute is missing. + # + # Alias for the <tt>read_attribute</tt> method. # # class Person < ActiveRecord::Base + # belongs_to :organization # end # # person = Person.new(name: 'Francesco', age: '22') # person[:name] # => "Francesco" # person[:age] # => 22 + # + # person = Person.select('id').first + # person[:name] # => ActiveModel::MissingAttributeError: missing attribute: name + # person[:organization_id] # => ActiveModel::MissingAttributeError: missing attribute: organization_id def [](attr_name) - read_attribute(attr_name) + read_attribute(attr_name) { |n| missing_attribute(n, caller) } end # Updates the attribute identified by <tt>attr_name</tt> with the specified +value+. diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb index 59f209cec8..0333605eac 100644 --- a/activerecord/lib/active_record/attribute_methods/dirty.rb +++ b/activerecord/lib/active_record/attribute_methods/dirty.rb @@ -2,11 +2,6 @@ require 'active_support/core_ext/module/attribute_accessors' require 'active_support/deprecation' module ActiveRecord - ActiveSupport.on_load(:active_record_config) do - mattr_accessor :partial_writes, instance_accessor: false - self.partial_writes = true - end - module AttributeMethods module Dirty # :nodoc: extend ActiveSupport::Concern @@ -18,7 +13,8 @@ module ActiveRecord raise "You cannot include Dirty after Timestamp" end - config_attribute :partial_writes + 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 diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb index 46fd6ebfb3..90701938e5 100644 --- a/activerecord/lib/active_record/attribute_methods/read.rb +++ b/activerecord/lib/active_record/attribute_methods/read.rb @@ -1,8 +1,4 @@ module ActiveRecord - ActiveSupport.on_load(:active_record_config) do - mattr_accessor :attribute_types_cached_by_default, instance_accessor: false - end - module AttributeMethods module Read extend ActiveSupport::Concern @@ -10,7 +6,8 @@ module ActiveRecord ATTRIBUTE_TYPES_CACHED_BY_DEFAULT = [:datetime, :timestamp, :time, :date] included do - config_attribute :attribute_types_cached_by_default + class_attribute :attribute_types_cached_by_default, instance_writer: false + self.attribute_types_cached_by_default = ATTRIBUTE_TYPES_CACHED_BY_DEFAULT end module ClassMethods @@ -35,36 +32,21 @@ 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). + # We want to generate the methods via module_eval rather than define_method, + # because define_method is slower on dispatch and uses more memory (because it + # creates a closure). # - # But sometimes the database might return columns with - # characters that are not allowed in normal method names (like - # 'my_column(omg)'. So to work around this we first define with - # the __temp__ identifier, and then use alias method to rename - # it to what we want. - # - # We are also defining a constant to hold the frozen string of - # the attribute name. Using a constant means that we do not have - # to allocate an object on each call to the attribute method. - # Making it frozen means that it doesn't get duped when used to - # key the @attributes_cache in read_attribute. - def define_method_attribute(name) - safe_name = name.unpack('h*').first + # But sometimes the database might return columns with characters that are not + # allowed in normal method names (like 'my_column(omg)'. So to work around this + # we first define with the __temp__ identifier, and then use alias method to + # rename it to what we want. + def define_method_attribute(attr_name) generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1 - module AttrNames - unless defined? ATTR_#{safe_name} - ATTR_#{safe_name} = #{name.inspect}.freeze - end + def __temp__ + read_attribute('#{attr_name}') { |n| missing_attribute(n, caller) } end - - def __temp__#{safe_name} - read_attribute(AttrNames::ATTR_#{safe_name}) { |n| missing_attribute(n, caller) } - end - - alias_method #{name.inspect}, :__temp__#{safe_name} - undef_method :__temp__#{safe_name} + alias_method '#{attr_name}', :__temp__ + undef_method :__temp__ STR end @@ -79,8 +61,6 @@ module ActiveRecord end end - ActiveRecord::Model.attribute_types_cached_by_default = ATTRIBUTE_TYPES_CACHED_BY_DEFAULT - # Returns the value of the attribute identified by <tt>attr_name</tt> after # it has been typecast (for example, "2004-12-12" in a data column is cast # to a date object, like Date.new(2004, 12, 12)). diff --git a/activerecord/lib/active_record/attribute_methods/serialization.rb b/activerecord/lib/active_record/attribute_methods/serialization.rb index 9994a81ede..5b9ed81424 100644 --- a/activerecord/lib/active_record/attribute_methods/serialization.rb +++ b/activerecord/lib/active_record/attribute_methods/serialization.rb @@ -45,7 +45,8 @@ module ActiveRecord end def serialized_attributes - ActiveSupport::Deprecation.warn("Instance level serialized_attributes method is deprecated, please use class level method.") + message = "Instance level serialized_attributes method is deprecated, please use class level method." + ActiveSupport::Deprecation.warn message defined?(@serialized_attributes) ? @serialized_attributes : self.class.serialized_attributes end @@ -118,6 +119,16 @@ module ActiveRecord super end end + + def attributes_before_type_cast + super.dup.tap do |attributes| + self.class.serialized_attributes.each_key do |key| + if attributes.key?(key) + attributes[key] = attributes[key].unserialized_value + end + end + end + end end end end diff --git a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb index f36a5806a9..806dc5b1d2 100644 --- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb +++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb @@ -1,13 +1,4 @@ - module ActiveRecord - ActiveSupport.on_load(:active_record_config) do - mattr_accessor :time_zone_aware_attributes, instance_accessor: false - self.time_zone_aware_attributes = false - - mattr_accessor :skip_time_zone_conversion_for_attributes, instance_accessor: false - self.skip_time_zone_conversion_for_attributes = [] - end - module AttributeMethods module TimeZoneConversion class Type # :nodoc: @@ -28,8 +19,11 @@ module ActiveRecord extend ActiveSupport::Concern included do - config_attribute :time_zone_aware_attributes, global: true - config_attribute :skip_time_zone_conversion_for_attributes + mattr_accessor :time_zone_aware_attributes, instance_writer: false + self.time_zone_aware_attributes = false + + class_attribute :skip_time_zone_conversion_for_attributes, instance_writer: false + self.skip_time_zone_conversion_for_attributes = [] end module ClassMethods diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb index cd33494cc3..fa9097db1f 100644 --- a/activerecord/lib/active_record/attribute_methods/write.rb +++ b/activerecord/lib/active_record/attribute_methods/write.rb @@ -9,19 +9,15 @@ module ActiveRecord module ClassMethods protected - - # See define_method_attribute in read.rb for an explanation of - # this code. - def define_method_attribute=(name) - safe_name = name.unpack('h*').first - generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1 - def __temp__#{safe_name}=(value) - write_attribute(AttrNames::ATTR_#{safe_name}, value) + def define_method_attribute=(attr_name) + if attr_name =~ ActiveModel::AttributeMethods::NAME_COMPILABLE_REGEXP + generated_attribute_methods.module_eval("def #{attr_name}=(new_value); write_attribute('#{attr_name}', new_value); end", __FILE__, __LINE__) + else + generated_attribute_methods.send(:define_method, "#{attr_name}=") do |new_value| + write_attribute(attr_name, new_value) + end end - alias_method #{(name + '=').inspect}, :__temp__#{safe_name}= - undef_method :__temp__#{safe_name}= - STR - end + end end # Updates the attribute identified by <tt>attr_name</tt> with the diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index a4705b24ca..eabbd80f66 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -321,8 +321,48 @@ module ActiveRecord #:nodoc: # So it's possible to assign a logger to the class through <tt>Base.logger=</tt> which will then be used by all # instances in the current object space. class Base - include ActiveRecord::Model + extend ActiveModel::Observing::ClassMethods + extend ActiveModel::Naming + + extend ActiveSupport::Benchmarkable + extend ActiveSupport::DescendantsTracker + + extend ConnectionHandling + extend QueryCache::ClassMethods + extend Querying + extend Translation + extend DynamicMatchers + extend Explain + extend ConnectionHandling + + include Persistence + include ReadonlyAttributes + include ModelSchema + include Inheritance + include Scoping + include Sanitization + include AttributeAssignment + include ActiveModel::Conversion + include Integration + include Validations + include CounterCache + include Locking::Optimistic + include Locking::Pessimistic + include AttributeMethods + include Callbacks + include ActiveModel::Observing + include Timestamp + include Associations + include ActiveModel::SecurePassword + include AutosaveAssociation + include NestedAttributes + include Aggregations + include Transactions + include Reflection + include Serialization + include Store + include Core end -end -ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Model::DeprecationProxy.new) + ActiveSupport.run_load_hooks(:active_record, Base) +end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb index 42bd16db80..1da95f451f 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -441,11 +441,11 @@ module ActiveRecord end def new_connection - ActiveRecord::Model.send(spec.adapter_method, spec.config) + Base.send(spec.adapter_method, spec.config) end def current_connection_id #:nodoc: - ActiveRecord::Model.connection_id ||= Thread.current.object_id + Base.connection_id ||= Thread.current.object_id end def checkout_new_connection @@ -567,10 +567,10 @@ module ActiveRecord class_to_pool[klass] ||= begin until pool = pool_for(klass) klass = klass.superclass - break unless klass < ActiveRecord::Tag + break unless klass <= Base end - class_to_pool[klass] = pool || pool_for(ActiveRecord::Model) + class_to_pool[klass] = pool end end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb index d61731633a..38960ab873 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -234,6 +234,10 @@ module ActiveRecord name = name.to_s type = type.to_sym + if primary_key_column_name == name + raise ArgumentError, "you can't redefine the primary key column '#{name}'. To define a custom primary key, pass { id: false } to create_table." + end + column = self[name] || new_column_definition(@base, name, type) limit = options.fetch(:limit) do @@ -302,6 +306,11 @@ module ActiveRecord definition end + def primary_key_column_name + primary_key_column = columns.detect { |c| c.type == :primary_key } + primary_key_column && primary_key_column.name + end + def native @base.native_database_types end diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index 0cb219767b..8517ce5fc5 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -3,7 +3,7 @@ require 'bigdecimal' require 'bigdecimal/util' require 'active_support/core_ext/benchmark' require 'active_record/connection_adapters/schema_cache' -require 'active_record/connection_adapters/abstract/schema_dumper' +require 'active_record/connection_adapters/abstract/schema_dumper' require 'monitor' require 'active_support/deprecation' @@ -263,7 +263,8 @@ module ActiveRecord end def transaction_joinable=(joinable) - ActiveSupport::Deprecation.warn "#transaction_joinable= is deprecated. Please pass the :joinable option to #begin_transaction instead." + message = "#transaction_joinable= is deprecated. Please pass the :joinable option to #begin_transaction instead." + ActiveSupport::Deprecation.warn message @transaction.joinable = joinable end diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb index 7da77af3a9..84e73e6f0f 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -498,6 +498,13 @@ module ActiveRecord # Maps logical Rails types to MySQL-specific data types. def type_to_sql(type, limit = nil, precision = nil, scale = nil) case type.to_s + when 'binary' + case limit + when 0..0xfff; "varbinary(#{limit})" + when nil; "blob" + when 0x1000..0xffffffff; "blob(#{limit})" + else raise(ActiveRecordError, "No binary type has character length #{limit}") + end when 'integer' case limit when 1; 'tinyint' diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb index 816b5e17c1..80984f39c9 100644 --- a/activerecord/lib/active_record/connection_adapters/column.rb +++ b/activerecord/lib/active_record/connection_adapters/column.rb @@ -94,7 +94,7 @@ module ActiveRecord case type when :string, :text then value - when :integer then value.to_i + when :integer then klass.value_to_integer(value) when :float then value.to_f when :decimal then klass.value_to_decimal(value) when :datetime, :timestamp then klass.string_to_time(value) @@ -107,14 +107,15 @@ module ActiveRecord end def type_cast_code(var_name) - ActiveSupport::Deprecation.warn("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.") + 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 "(#{var_name}.to_i)" + 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})" @@ -197,6 +198,17 @@ module ActiveRecord end end + # Used to convert values to integer. + # handle the case when an integer column is used to store boolean values + def value_to_integer(value) + case value + when TrueClass, FalseClass + value ? 1 : 0 + else + value.to_i + end + end + # convert something to a BigDecimal def value_to_decimal(value) # Using .class is faster than .is_a? and diff --git a/activerecord/lib/active_record/connection_adapters/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/connection_specification.rb index b9a61f7d91..09250d3c01 100644 --- a/activerecord/lib/active_record/connection_adapters/connection_specification.rb +++ b/activerecord/lib/active_record/connection_adapters/connection_specification.rb @@ -73,6 +73,8 @@ module ActiveRecord :database => config.path.sub(%r{^/},""), :host => config.host } spec.reject!{ |_,value| value.blank? } + uri_parser = URI::Parser.new + spec.map { |key,value| spec[key] = uri_parser.unescape(value) if value.is_a?(String) } if config.query options = Hash[config.query.split("&").map{ |pair| pair.split("=") }].symbolize_keys spec.merge!(options) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb index 553985bd1e..34d7a246b2 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb @@ -221,10 +221,9 @@ module ActiveRecord end def outside_transaction? - ActiveSupport::Deprecation.warn( - "#outside_transaction? is deprecated. This method was only really used " \ - "internally, but you can use #transaction_open? instead." - ) + message = "#outside_transaction? is deprecated. This method was only really used " \ + "internally, but you can use #transaction_open? instead." + ActiveSupport::Deprecation.warn message @connection.transaction_status == PGconn::PQTRANS_IDLE end diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index 4d5cb72c67..b89e9a01a8 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -251,7 +251,7 @@ module ActiveRecord value = super if column.type == :string && value.encoding == Encoding::ASCII_8BIT logger.error "Binary data inserted for `string` type on column `#{column.name}`" if logger - value.encode! 'utf-8' + value = value.encode Encoding::UTF_8 end value end @@ -519,24 +519,22 @@ module ActiveRecord def copy_table(from, to, options = {}) #:nodoc: from_primary_key = primary_key(from) - options[:primary_key] = from_primary_key if from_primary_key != 'id' - unless options[:primary_key] - options[:id] = columns(from).detect{|c| c.name == 'id'}.present? && from_primary_key == 'id' - end + options[:id] = false create_table(to, options) do |definition| @definition = definition + @definition.primary_key(from_primary_key) if from_primary_key.present? columns(from).each do |column| column_name = options[:rename] ? (options[:rename][column.name] || options[:rename][column.name.to_sym] || column.name) : column.name + next if column_name == from_primary_key @definition.column(column_name, column.type, :limit => column.limit, :default => column.default, :precision => column.precision, :scale => column.scale, :null => column.null) end - @definition.primary_key(from_primary_key) if from_primary_key yield @definition if block_given? end diff --git a/activerecord/lib/active_record/connection_handling.rb b/activerecord/lib/active_record/connection_handling.rb index 3531be05bf..8ce7a7a463 100644 --- a/activerecord/lib/active_record/connection_handling.rb +++ b/activerecord/lib/active_record/connection_handling.rb @@ -1,4 +1,3 @@ - module ActiveRecord module ConnectionHandling # Establishes the connection to the database. Accepts a hash as input where diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb index f97c363871..98032db2ef 100644 --- a/activerecord/lib/active_record/core.rb +++ b/activerecord/lib/active_record/core.rb @@ -4,73 +4,6 @@ require 'thread' module ActiveRecord ActiveSupport.on_load(:active_record_config) do - ## - # :singleton-method: - # - # Accepts a logger conforming to the interface of Log4r which is then - # passed on to any new database connections made and which can be - # retrieved on both a class and instance level by calling +logger+. - mattr_accessor :logger, instance_accessor: false - - ## - # :singleton-method: - # Contains the database configuration - as is typically stored in config/database.yml - - # as a Hash. - # - # For example, the following database.yml... - # - # development: - # adapter: sqlite3 - # database: db/development.sqlite3 - # - # production: - # adapter: sqlite3 - # database: db/production.sqlite3 - # - # ...would result in ActiveRecord::Base.configurations to look like this: - # - # { - # 'development' => { - # 'adapter' => 'sqlite3', - # 'database' => 'db/development.sqlite3' - # }, - # 'production' => { - # 'adapter' => 'sqlite3', - # 'database' => 'db/production.sqlite3' - # } - # } - mattr_accessor :configurations, instance_accessor: false - self.configurations = {} - - ## - # :singleton-method: - # Determines whether to use Time.utc (using :utc) or Time.local (using :local) when pulling - # dates and times from the database. This is set to :utc by default. - mattr_accessor :default_timezone, instance_accessor: false - self.default_timezone = :utc - - ## - # :singleton-method: - # Specifies the format to use when dumping the database schema with Rails' - # Rakefile. If :sql, the schema is dumped as (potentially database- - # specific) SQL statements. If :ruby, the schema is dumped as an - # ActiveRecord::Schema file which can be loaded into any database that - # supports migrations. Use :ruby if you want to have different database - # adapters for, e.g., your development and test environments. - mattr_accessor :schema_format, instance_accessor: false - self.schema_format = :ruby - - ## - # :singleton-method: - # Specify whether or not to use timestamps for migration versions - mattr_accessor :timestamped_migrations, instance_accessor: false - self.timestamped_migrations = true - - mattr_accessor :connection_handler, instance_accessor: false - self.connection_handler = ConnectionAdapters::ConnectionHandler.new - - mattr_accessor :dependent_restrict_raises, instance_accessor: false - self.dependent_restrict_raises = true end module Core @@ -79,12 +12,68 @@ module ActiveRecord included do ## # :singleton-method: - # The connection handler - config_attribute :connection_handler + # + # Accepts a logger conforming to the interface of Log4r which is then + # passed on to any new database connections made and which can be + # retrieved on both a class and instance level by calling +logger+. + mattr_accessor :logger, instance_writer: false + + ## + # :singleton-method: + # Contains the database configuration - as is typically stored in config/database.yml - + # as a Hash. + # + # For example, the following database.yml... + # + # development: + # adapter: sqlite3 + # database: db/development.sqlite3 + # + # production: + # adapter: sqlite3 + # database: db/production.sqlite3 + # + # ...would result in ActiveRecord::Base.configurations to look like this: + # + # { + # 'development' => { + # 'adapter' => 'sqlite3', + # 'database' => 'db/development.sqlite3' + # }, + # 'production' => { + # 'adapter' => 'sqlite3', + # 'database' => 'db/production.sqlite3' + # } + # } + mattr_accessor :configurations, instance_writer: false + self.configurations = {} - %w(logger configurations default_timezone schema_format timestamped_migrations).each do |name| - config_attribute name, global: true - end + ## + # :singleton-method: + # Determines whether to use Time.utc (using :utc) or Time.local (using :local) when pulling + # dates and times from the database. This is set to :utc by default. + mattr_accessor :default_timezone, instance_writer: false + self.default_timezone = :utc + + ## + # :singleton-method: + # Specifies the format to use when dumping the database schema with Rails' + # Rakefile. If :sql, the schema is dumped as (potentially database- + # specific) SQL statements. If :ruby, the schema is dumped as an + # ActiveRecord::Schema file which can be loaded into any database that + # supports migrations. Use :ruby if you want to have different database + # adapters for, e.g., your development and test environments. + mattr_accessor :schema_format, instance_writer: false + self.schema_format = :ruby + + ## + # :singleton-method: + # Specify whether or not to use timestamps for migration versions + mattr_accessor :timestamped_migrations, instance_writer: false + self.timestamped_migrations = true + + class_attribute :connection_handler, instance_writer: false + self.connection_handler = ConnectionAdapters::ConnectionHandler.new end module ClassMethods @@ -139,7 +128,13 @@ module ActiveRecord # Returns the Arel engine. def arel_engine - @arel_engine ||= connection_handler.retrieve_connection_pool(self) ? self : active_record_super.arel_engine + @arel_engine ||= begin + if Base == self || connection_handler.retrieve_connection_pool(self) + self + else + superclass.arel_engine + end + end end private diff --git a/activerecord/lib/active_record/explain.rb b/activerecord/lib/active_record/explain.rb index 9e0390bed1..af772996f1 100644 --- a/activerecord/lib/active_record/explain.rb +++ b/activerecord/lib/active_record/explain.rb @@ -1,12 +1,10 @@ require 'active_support/lazy_load_hooks' module ActiveRecord - ActiveSupport.on_load(:active_record_config) do - mattr_accessor :auto_explain_threshold_in_seconds, instance_accessor: false - end - module Explain - delegate :auto_explain_threshold_in_seconds, :auto_explain_threshold_in_seconds=, to: 'ActiveRecord::Model' + def self.extended(base) + base.mattr_accessor :auto_explain_threshold_in_seconds, instance_accessor: false + end # If auto explain is enabled, this method triggers EXPLAIN logging for the # queries triggered by the block if it takes more than the threshold as a diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index 7f1d62af39..29a99a5336 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -553,7 +553,7 @@ module ActiveRecord rows[table_name] = fixtures.map do |label, fixture| row = fixture.to_hash - if model_class && model_class < ActiveRecord::Model + if model_class && model_class < ActiveRecord::Base # fill in timestamp columns if they aren't specified and the model is set to record_timestamps if model_class.record_timestamps timestamp_column_names.each do |c_name| diff --git a/activerecord/lib/active_record/inheritance.rb b/activerecord/lib/active_record/inheritance.rb index 35273b0d81..a448fa1f5c 100644 --- a/activerecord/lib/active_record/inheritance.rb +++ b/activerecord/lib/active_record/inheritance.rb @@ -1,29 +1,22 @@ - module ActiveRecord - ActiveSupport.on_load(:active_record_config) do - # Determine whether to store the full constant name including namespace when using STI - mattr_accessor :store_full_sti_class, instance_accessor: false - self.store_full_sti_class = true - end - module Inheritance extend ActiveSupport::Concern included do - config_attribute :store_full_sti_class + # Determine whether to store the full constant name including namespace when using STI + class_attribute :store_full_sti_class, instance_writer: false + self.store_full_sti_class = true end module ClassMethods # True if this isn't a concrete subclass needing a STI type condition. def descends_from_active_record? - sup = active_record_super - - if sup.abstract_class? - sup.descends_from_active_record? - elsif self == Base + if self == Base false + elsif superclass.abstract_class? + superclass.descends_from_active_record? else - [Base, Model].include?(sup) || !columns_hash.include?(inheritance_column) + superclass == Base || !columns_hash.include?(inheritance_column) end end @@ -40,9 +33,8 @@ module ActiveRecord @symbolized_sti_name ||= sti_name.present? ? sti_name.to_sym : symbolized_base_class end - # Returns the class descending directly from ActiveRecord::Base (or - # that includes ActiveRecord::Model), or an abstract class, if any, in - # the inheritance hierarchy. + # Returns the class descending directly from ActiveRecord::Base, or + # an abstract class, if any, in the inheritance hierarchy. # # If A extends AR::Base, A.base_class will return A. If B descends from A # through some arbitrarily deep hierarchy, B.base_class will return A. @@ -50,15 +42,14 @@ module ActiveRecord # If B < A and C < B and if A is an abstract_class then both B.base_class # and C.base_class would return B as the answer since A is an abstract_class. def base_class - unless self < ActiveRecord::Tag + unless self < Base raise ActiveRecordError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord" end - sup = active_record_super - if sup == Base || sup == Model || sup.abstract_class? + if superclass == Base || superclass.abstract_class? self else - sup.base_class + superclass.base_class end end @@ -97,14 +88,6 @@ module ActiveRecord sti_class.allocate.init_with('attributes' => record, 'column_types' => column_types) end - # For internal use. - # - # If this class includes ActiveRecord::Model then it won't have a - # superclass. So this provides a way to get to the 'root' (ActiveRecord::Model). - def active_record_super #:nodoc: - superclass < Model ? superclass : Model - end - protected # Returns the class type of the record using the current module as a prefix. So descendants of diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb index e96ed00f9c..035c77c424 100644 --- a/activerecord/lib/active_record/locking/optimistic.rb +++ b/activerecord/lib/active_record/locking/optimistic.rb @@ -1,9 +1,4 @@ module ActiveRecord - ActiveSupport.on_load(:active_record_config) do - mattr_accessor :lock_optimistically, instance_accessor: false - self.lock_optimistically = true - end - module Locking # == What is Optimistic Locking # @@ -56,7 +51,8 @@ module ActiveRecord extend ActiveSupport::Concern included do - config_attribute :lock_optimistically + class_attribute :lock_optimistically, instance_writer: false + self.lock_optimistically = true end def locking_enabled? #:nodoc: diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index d5ee98382d..5499f37802 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -342,7 +342,9 @@ module ActiveRecord end def call(env) - ActiveRecord::Migration.check_pending! + ActiveRecord::Base.logger.quietly do + ActiveRecord::Migration.check_pending! + end @app.call(env) end end @@ -730,9 +732,8 @@ module ActiveRecord running = runnable if block_given? - ActiveSupport::Deprecation.warn(<<-eomsg) -block argument to migrate is deprecated, please filter migrations before constructing the migrator - eomsg + message = "block argument to migrate is deprecated, please filter migrations before constructing the migrator" + ActiveSupport::Deprecation.warn message running.select! { |m| yield m } end diff --git a/activerecord/lib/active_record/model.rb b/activerecord/lib/active_record/model.rb deleted file mode 100644 index f059840f4d..0000000000 --- a/activerecord/lib/active_record/model.rb +++ /dev/null @@ -1,167 +0,0 @@ -require 'active_support/core_ext/module/attribute_accessors' - -module ActiveRecord - module Configuration # :nodoc: - # This just abstracts out how we define configuration options in AR. Essentially we - # have mattr_accessors on the ActiveRecord:Model constant that define global defaults. - # Classes that then use AR get class_attributes defined, which means that when they - # are assigned the default will be overridden for that class and subclasses. (Except - # when options[:global] == true, in which case there is one global value always.) - def config_attribute(name, options = {}) - if options[:global] - class_eval <<-CODE, __FILE__, __LINE__ + 1 - def self.#{name}; ActiveRecord::Model.#{name}; end - def #{name}; ActiveRecord::Model.#{name}; end - def self.#{name}=(val); ActiveRecord::Model.#{name} = val; end - CODE - else - options[:instance_writer] ||= false - class_attribute name, options - - singleton_class.class_eval <<-CODE, __FILE__, __LINE__ + 1 - remove_method :#{name} - def #{name}; ActiveRecord::Model.#{name}; end - CODE - end - end - end - - # This allows us to detect an ActiveRecord::Model while it's in the process of - # being included. - module Tag; end - - # <tt>ActiveRecord::Model</tt> can be included into a class to add Active Record - # persistence. This is an alternative to inheriting from <tt>ActiveRecord::Base</tt>. - # - # class Post - # include ActiveRecord::Model - # end - module Model - extend ActiveSupport::Concern - extend ConnectionHandling - extend ActiveModel::Observing::ClassMethods - - def self.append_features(base) - base.class_eval do - include Tag - extend Configuration - end - - super - end - - included do - extend ActiveModel::Naming - extend ActiveSupport::Benchmarkable - extend ActiveSupport::DescendantsTracker - - extend QueryCache::ClassMethods - extend Querying - extend Translation - extend DynamicMatchers - extend Explain - extend ConnectionHandling - - initialize_generated_modules unless self == Base - end - - include Persistence - include ReadonlyAttributes - include ModelSchema - include Inheritance - include Scoping - include Sanitization - include AttributeAssignment - include ActiveModel::Conversion - include Integration - include Validations - include CounterCache - include Locking::Optimistic - include Locking::Pessimistic - include AttributeMethods - include Callbacks - include ActiveModel::Observing - include Timestamp - include Associations - include ActiveModel::SecurePassword - include AutosaveAssociation - include NestedAttributes - include Aggregations - include Transactions - include Reflection - include Serialization - include Store - include Core - - class << self - def arel_engine - self - end - - def abstract_class? - false - end - - # Defines the name of the table column which will store the class name on single-table - # inheritance situations. - # - # The default inheritance column name is +type+, which means it's a - # reserved word inside Active Record. To be able to use single-table - # inheritance with another column name, or to use the column +type+ in - # your own model for something else, you can set +inheritance_column+: - # - # self.inheritance_column = 'zoink' - def inheritance_column - 'type' - end - end - - class DeprecationProxy < BasicObject #:nodoc: - def initialize(model = Model, base = Base) - @model = model - @base = base - end - - def method_missing(name, *args, &block) - if @model.respond_to?(name, true) - @model.send(name, *args, &block) - else - ::ActiveSupport::Deprecation.warn( - "The object passed to the active_record load hook was previously ActiveRecord::Base " \ - "(a Class). Now it is ActiveRecord::Model (a Module). You have called `#{name}' which " \ - "is only defined on ActiveRecord::Base. Please change your code so that it works with " \ - "a module rather than a class. (Model is included in Base, so anything added to Model " \ - "will be available on Base as well.)" - ) - @base.send(name, *args, &block) - end - end - - alias send method_missing - - def extend(*mods) - ::ActiveSupport::Deprecation.warn( - "The object passed to the active_record load hook was previously ActiveRecord::Base " \ - "(a Class). Now it is ActiveRecord::Model (a Module). You have called `extend' which " \ - "would add singleton methods to Model. This is presumably not what you want, since the " \ - "methods would not be inherited down to Base. Rather than using extend, please use " \ - "ActiveSupport::Concern + include, which will ensure that your class methods are " \ - "inherited." - ) - @base.extend(*mods) - end - end - end - - # This hook is where config accessors on Model get defined. - # - # We don't want to just open the Model module and add stuff to it in other files, because - # that would cause Model to load, which causes all sorts of loading order issues. - # - # We need this hook rather than just using the :active_record one, because users of the - # :active_record hook may need to use config options. - ActiveSupport.run_load_hooks(:active_record_config, Model) - - # Load Base at this point, because the active_record load hook is run in that file. - Base -end diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb index 99de16cd33..1b95b72c8a 100644 --- a/activerecord/lib/active_record/model_schema.rb +++ b/activerecord/lib/active_record/model_schema.rb @@ -1,18 +1,4 @@ - module ActiveRecord - ActiveSupport.on_load(:active_record_config) do - mattr_accessor :primary_key_prefix_type, instance_accessor: false - - mattr_accessor :table_name_prefix, instance_accessor: false - self.table_name_prefix = "" - - mattr_accessor :table_name_suffix, instance_accessor: false - self.table_name_suffix = "" - - mattr_accessor :pluralize_table_names, instance_accessor: false - self.pluralize_table_names = true - end - module ModelSchema extend ActiveSupport::Concern @@ -24,7 +10,7 @@ module ActiveRecord # the Product class will look for "productid" instead of "id" as the primary column. If the # latter is specified, the Product class will look for "product_id" instead of "id". Remember # that this is a global setting for all Active Records. - config_attribute :primary_key_prefix_type, global: true + mattr_accessor :primary_key_prefix_type, instance_writer: false ## # :singleton-method: @@ -36,20 +22,25 @@ module ActiveRecord # If you are organising your models within modules you can add a prefix to the models within # a namespace by defining a singleton method in the parent module called table_name_prefix which # returns your chosen prefix. - config_attribute :table_name_prefix + class_attribute :table_name_prefix, instance_writer: false + self.table_name_prefix = "" ## # :singleton-method: # Works like +table_name_prefix+, but appends instead of prepends (set to "_basecamp" gives "projects_basecamp", # "people_basecamp"). By default, the suffix is the empty string. - config_attribute :table_name_suffix + class_attribute :table_name_suffix, instance_writer: false + self.table_name_suffix = "" ## # :singleton-method: # Indicates whether table names should be the pluralized versions of the corresponding class names. # If true, the default table name for a Product class will be +products+. If false, it would just be +product+. # See table_name for the full rules on table/class naming. This is true, by default. - config_attribute :pluralize_table_names + class_attribute :pluralize_table_names, instance_writer: false + self.pluralize_table_names = true + + self.inheritance_column = 'type' end module ClassMethods @@ -144,9 +135,9 @@ module ActiveRecord # Computes the table name, (re)sets it internally, and returns it. def reset_table_name #:nodoc: self.table_name = if abstract_class? - active_record_super == Base ? nil : active_record_super.table_name - elsif active_record_super.abstract_class? - active_record_super.table_name || compute_table_name + superclass == Base ? nil : superclass.table_name + elsif superclass.abstract_class? + superclass.table_name || compute_table_name else compute_table_name end @@ -156,9 +147,17 @@ module ActiveRecord (parents.detect{ |p| p.respond_to?(:table_name_prefix) } || self).table_name_prefix end - # The name of the column containing the object's class when Single Table Inheritance is used + # Defines the name of the table column which will store the class name on single-table + # inheritance situations. + # + # The default inheritance column name is +type+, which means it's a + # reserved word inside Active Record. To be able to use single-table + # inheritance with another column name, or to use the column +type+ in + # your own model for something else, you can set +inheritance_column+: + # + # self.inheritance_column = 'zoink' def inheritance_column - (@inheritance_column ||= nil) || active_record_super.inheritance_column + (@inheritance_column ||= nil) || superclass.inheritance_column end # Sets the value of inheritance_column @@ -331,7 +330,7 @@ module ActiveRecord base = base_class if self == base # Nested classes are prefixed with singular parent table name. - if parent < ActiveRecord::Model && !parent.abstract_class? + if parent < Base && !parent.abstract_class? contained = parent.table_name contained = contained.singularize if parent.pluralize_table_names contained += '_' diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index 43f908ae5f..aba56c2861 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -3,11 +3,6 @@ require 'active_support/core_ext/object/try' require 'active_support/core_ext/hash/indifferent_access' module ActiveRecord - ActiveSupport.on_load(:active_record_config) do - mattr_accessor :nested_attributes_options, instance_accessor: false - self.nested_attributes_options = {} - end - module NestedAttributes #:nodoc: class TooManyRecords < ActiveRecordError end @@ -15,7 +10,8 @@ module ActiveRecord extend ActiveSupport::Concern included do - config_attribute :nested_attributes_options + class_attribute :nested_attributes_options, instance_writer: false + self.nested_attributes_options = {} end # = Active Record Nested Attributes diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 65a31f1566..8e749772a1 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -224,11 +224,13 @@ module ActiveRecord verify_readonly_attribute(key.to_s) end + updated_count = self.class.where(self.class.primary_key => id).update_all(attributes) + attributes.each do |k,v| raw_write_attribute(k,v) end - self.class.where(self.class.primary_key => id).update_all(attributes) == 1 + updated_count == 1 end # Initializes +attribute+ to zero if +nil+ and adds the value passed as +by+ (default is 1). diff --git a/activerecord/lib/active_record/query_cache.rb b/activerecord/lib/active_record/query_cache.rb index 2bd8ecda20..38e18b32a4 100644 --- a/activerecord/lib/active_record/query_cache.rb +++ b/activerecord/lib/active_record/query_cache.rb @@ -5,19 +5,19 @@ module ActiveRecord module ClassMethods # Enable the query cache within the block if Active Record is configured. def cache(&block) - if ActiveRecord::Base.configurations.blank? - yield - else + if ActiveRecord::Base.connected? connection.cache(&block) + else + yield end end # Disable the query cache within the block if Active Record is configured. def uncached(&block) - if ActiveRecord::Base.configurations.blank? - yield - else + if ActiveRecord::Base.connected? connection.uncached(&block) + else + yield end end end diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index d7e35fb771..77e41ea927 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -80,7 +80,7 @@ module ActiveRecord if File.file?(filename) cache = Marshal.load File.binread filename if cache.version == ActiveRecord::Migrator.current_version - ActiveRecord::Model.connection.schema_cache = cache + self.connection.schema_cache = cache else warn "Ignoring db/schema_cache.dump because it has expired. The current schema version is #{ActiveRecord::Migrator.current_version}, but the one in the cache is #{cache.version}." end @@ -122,8 +122,8 @@ module ActiveRecord ActiveSupport.on_load(:active_record) do ActionDispatch::Reloader.send(hook) do - ActiveRecord::Model.clear_reloadable_connections! - ActiveRecord::Model.clear_cache! + ActiveRecord::Base.clear_reloadable_connections! + ActiveRecord::Base.clear_cache! end end end @@ -135,13 +135,12 @@ module ActiveRecord config.after_initialize do |app| ActiveSupport.on_load(:active_record) do - ActiveRecord::Model.instantiate_observers + instantiate_observers ActionDispatch::Reloader.to_prepare do - ActiveRecord::Model.instantiate_observers + ActiveRecord::Base.instantiate_observers end end - end end end diff --git a/activerecord/lib/active_record/readonly_attributes.rb b/activerecord/lib/active_record/readonly_attributes.rb index b3c20c4aff..8499bb16e7 100644 --- a/activerecord/lib/active_record/readonly_attributes.rb +++ b/activerecord/lib/active_record/readonly_attributes.rb @@ -22,7 +22,8 @@ module ActiveRecord end def _attr_readonly - ActiveSupport::Deprecation.warn("Instance level _attr_readonly method is deprecated, please use class level method.") + message = "Instance level _attr_readonly method is deprecated, please use class level method." + ActiveSupport::Deprecation.warn message defined?(@_attr_readonly) ? @_attr_readonly : self.class._attr_readonly end end diff --git a/activerecord/lib/active_record/relation/batches.rb b/activerecord/lib/active_record/relation/batches.rb index 8af0c6a8ef..b921f2eddb 100644 --- a/activerecord/lib/active_record/relation/batches.rb +++ b/activerecord/lib/active_record/relation/batches.rb @@ -62,11 +62,11 @@ module ActiveRecord ActiveRecord::Base.logger.warn("Scoped order and limit are ignored, it's forced to be batch order and batch size") end - start = options.delete(:start) || 0 + start = options.delete(:start) batch_size = options.delete(:batch_size) || 1000 relation = relation.reorder(batch_order).limit(batch_size) - records = relation.where(table[primary_key].gteq(start)).to_a + records = start ? relation.where(table[primary_key].gteq(start)).to_a : relation.to_a while records.any? records_size = records.size diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 84aaa39fed..99c2f45bc8 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -159,7 +159,7 @@ module ActiveRecord # Person.exists?(false) # Person.exists? def exists?(conditions = :none) - conditions = conditions.id if ActiveRecord::Model === conditions + conditions = conditions.id if Base === conditions return false if !conditions join_dependency = construct_join_dependency_for_association_find diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb index 71030cb5d7..bd7aeb0c4e 100644 --- a/activerecord/lib/active_record/relation/predicate_builder.rb +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -40,7 +40,7 @@ module ActiveRecord # # For polymorphic relationships, find the foreign key and type: # PriceEstimate.where(:estimate_of => treasure) - if klass && value.class < ActiveRecord::Tag && reflection = klass.reflect_on_association(column.to_sym) + if klass && value.class < Base && reflection = klass.reflect_on_association(column.to_sym) if reflection.polymorphic? queries << build(table[reflection.foreign_type], value.class.base_class) end @@ -67,7 +67,7 @@ module ActiveRecord def self.build(attribute, value) case value when Array, ActiveRecord::Associations::CollectionProxy - values = value.to_a.map {|x| x.is_a?(ActiveRecord::Model) ? x.id : x} + values = value.to_a.map {|x| x.is_a?(Base) ? x.id : x} ranges, values = values.partition {|v| v.is_a?(Range)} values_predicate = if values.include?(nil) @@ -93,7 +93,7 @@ module ActiveRecord attribute.in(value.arel.ast) when Range attribute.in(value) - when ActiveRecord::Model + when ActiveRecord::Base attribute.eq(value.id) when Class # FIXME: I think we need to deprecate this behavior diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 14bcb337e9..4fdc296c7e 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -496,6 +496,11 @@ module ActiveRecord extending(NullRelation) end + # Like #none, but modifies relation in place. + def none! + extending!(NullRelation) + end + # Sets readonly attributes for the returned relation. If value is # true (default), attempting to update a record will result in an error. # diff --git a/activerecord/lib/active_record/result.rb b/activerecord/lib/active_record/result.rb index 425b9b41d8..bea195e9b8 100644 --- a/activerecord/lib/active_record/result.rb +++ b/activerecord/lib/active_record/result.rb @@ -56,7 +56,7 @@ module ActiveRecord @hash_rows ||= begin # We freeze the strings to prevent them getting duped when - # used as keys in ActiveRecord::Model's @attributes hash + # used as keys in ActiveRecord::Base's @attributes hash columns = @columns.map { |c| c.dup.freeze } @rows.map { |row| Hash[columns.zip(row)] diff --git a/activerecord/lib/active_record/serialization.rb b/activerecord/lib/active_record/serialization.rb index e8dd312a47..6b55af4205 100644 --- a/activerecord/lib/active_record/serialization.rb +++ b/activerecord/lib/active_record/serialization.rb @@ -1,19 +1,11 @@ module ActiveRecord #:nodoc: - ActiveSupport.on_load(:active_record_config) do - mattr_accessor :include_root_in_json, instance_accessor: false - self.include_root_in_json = true - end - # = Active Record Serialization module Serialization extend ActiveSupport::Concern include ActiveModel::Serializers::JSON included do - singleton_class.class_eval do - remove_method :include_root_in_json - delegate :include_root_in_json, to: 'ActiveRecord::Model' - end + self.include_root_in_json = true end def serializable_hash(options = nil) diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb index ec4588f601..920d6848c1 100644 --- a/activerecord/lib/active_record/timestamp.rb +++ b/activerecord/lib/active_record/timestamp.rb @@ -1,8 +1,6 @@ module ActiveRecord ActiveSupport.on_load(:active_record_config) do - mattr_accessor :record_timestamps, instance_accessor: false - self.record_timestamps = true end # = Active Record Timestamp @@ -37,7 +35,8 @@ module ActiveRecord extend ActiveSupport::Concern included do - config_attribute :record_timestamps, instance_writer: true + class_attribute :record_timestamps + self.record_timestamps = true end def initialize_dup(other) # :nodoc: diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb index 934393b4e7..f91abfbd19 100644 --- a/activerecord/lib/active_record/transactions.rb +++ b/activerecord/lib/active_record/transactions.rb @@ -328,6 +328,7 @@ module ActiveRecord @_start_transaction_state[:new_record] = @new_record @_start_transaction_state[:destroyed] = @destroyed @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) + 1 + @_start_transaction_state[:frozen?] = @attributes.frozen? end # Clear the new record state and id of a record. @@ -342,8 +343,8 @@ module ActiveRecord @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1 if @_start_transaction_state[:level] < 1 || force restore_state = @_start_transaction_state - was_frozen = @attributes.frozen? - @attributes = @attributes.dup if was_frozen + was_frozen = restore_state[:frozen?] + @attributes = @attributes.dup if @attributes.frozen? @new_record = restore_state[:new_record] @destroyed = restore_state[:destroyed] if restore_state.has_key?(:id) diff --git a/activerecord/test/cases/adapter_test.rb b/activerecord/test/cases/adapter_test.rb index 93b01a3934..404b492288 100644 --- a/activerecord/test/cases/adapter_test.rb +++ b/activerecord/test/cases/adapter_test.rb @@ -69,16 +69,16 @@ module ActiveRecord def test_not_specifying_database_name_for_cross_database_selects begin assert_nothing_raised do - ActiveRecord::Model.establish_connection(ActiveRecord::Base.configurations['arunit'].except(:database)) + ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['arunit'].except(:database)) config = ARTest.connection_config - ActiveRecord::Model.connection.execute( + ActiveRecord::Base.connection.execute( "SELECT #{config['arunit']['database']}.pirates.*, #{config['arunit2']['database']}.courses.* " \ "FROM #{config['arunit']['database']}.pirates, #{config['arunit2']['database']}.courses" ) end ensure - ActiveRecord::Model.establish_connection 'arunit' + ActiveRecord::Base.establish_connection 'arunit' end end end diff --git a/activerecord/test/cases/adapters/mysql/connection_test.rb b/activerecord/test/cases/adapters/mysql/connection_test.rb index 4bccd2cc59..534dc2c2df 100644 --- a/activerecord/test/cases/adapters/mysql/connection_test.rb +++ b/activerecord/test/cases/adapters/mysql/connection_test.rb @@ -3,13 +3,13 @@ require "cases/helper" class MysqlConnectionTest < ActiveRecord::TestCase def setup super - @connection = ActiveRecord::Model.connection + @connection = ActiveRecord::Base.connection end def test_mysql_reconnect_attribute_after_connection_with_reconnect_true run_without_connection do |orig_connection| - ActiveRecord::Model.establish_connection(orig_connection.merge({:reconnect => true})) - assert ActiveRecord::Model.connection.raw_connection.reconnect + ActiveRecord::Base.establish_connection(orig_connection.merge({:reconnect => true})) + assert ActiveRecord::Base.connection.raw_connection.reconnect end end @@ -25,8 +25,8 @@ class MysqlConnectionTest < ActiveRecord::TestCase def test_mysql_reconnect_attribute_after_connection_with_reconnect_false run_without_connection do |orig_connection| - ActiveRecord::Model.establish_connection(orig_connection.merge({:reconnect => false})) - assert !ActiveRecord::Model.connection.raw_connection.reconnect + ActiveRecord::Base.establish_connection(orig_connection.merge({:reconnect => false})) + assert !ActiveRecord::Base.connection.raw_connection.reconnect end end @@ -117,7 +117,7 @@ class MysqlConnectionTest < ActiveRecord::TestCase # Test that MySQL allows multiple results for stored procedures if defined?(Mysql) && Mysql.const_defined?(:CLIENT_MULTI_RESULTS) def test_multi_results - rows = ActiveRecord::Model.connection.select_rows('CALL ten();') + rows = ActiveRecord::Base.connection.select_rows('CALL ten();') assert_equal 10, rows[0][0].to_i, "ten() did not return 10 as expected: #{rows.inspect}" assert @connection.active?, "Bad connection use by 'MysqlAdapter.select_rows'" end @@ -130,9 +130,9 @@ class MysqlConnectionTest < ActiveRecord::TestCase def test_mysql_strict_mode_disabled_dont_override_global_sql_mode run_without_connection do |orig_connection| - ActiveRecord::Model.establish_connection(orig_connection.merge({:strict => false})) - global_sql_mode = ActiveRecord::Model.connection.exec_query "SELECT @@GLOBAL.sql_mode" - session_sql_mode = ActiveRecord::Model.connection.exec_query "SELECT @@SESSION.sql_mode" + ActiveRecord::Base.establish_connection(orig_connection.merge({:strict => false})) + global_sql_mode = ActiveRecord::Base.connection.exec_query "SELECT @@GLOBAL.sql_mode" + session_sql_mode = ActiveRecord::Base.connection.exec_query "SELECT @@SESSION.sql_mode" assert_equal global_sql_mode.rows, session_sql_mode.rows end end @@ -140,11 +140,11 @@ class MysqlConnectionTest < ActiveRecord::TestCase private def run_without_connection - original_connection = ActiveRecord::Model.remove_connection + original_connection = ActiveRecord::Base.remove_connection begin yield original_connection ensure - ActiveRecord::Model.establish_connection(original_connection) + ActiveRecord::Base.establish_connection(original_connection) end end end diff --git a/activerecord/test/cases/adapters/mysql/sql_types_test.rb b/activerecord/test/cases/adapters/mysql/sql_types_test.rb new file mode 100644 index 0000000000..1ddb1b91c9 --- /dev/null +++ b/activerecord/test/cases/adapters/mysql/sql_types_test.rb @@ -0,0 +1,14 @@ +require "cases/helper" + +class SqlTypesTest < ActiveRecord::TestCase + def test_binary_types + assert_equal 'varbinary(64)', type_to_sql(:binary, 64) + assert_equal 'varbinary(4095)', type_to_sql(:binary, 4095) + assert_equal 'blob(4096)', type_to_sql(:binary, 4096) + assert_equal 'blob', type_to_sql(:binary) + end + + def type_to_sql(*args) + ActiveRecord::Base.connection.type_to_sql(*args) + end +end diff --git a/activerecord/test/cases/adapters/mysql2/connection_test.rb b/activerecord/test/cases/adapters/mysql2/connection_test.rb index c63e4fe5b6..14c22d2519 100644 --- a/activerecord/test/cases/adapters/mysql2/connection_test.rb +++ b/activerecord/test/cases/adapters/mysql2/connection_test.rb @@ -3,7 +3,7 @@ require "cases/helper" class MysqlConnectionTest < ActiveRecord::TestCase def setup super - @connection = ActiveRecord::Model.connection + @connection = ActiveRecord::Base.connection @connection.extend(LogIntercepter) @connection.intercepted = true end @@ -46,9 +46,9 @@ class MysqlConnectionTest < ActiveRecord::TestCase def test_mysql_strict_mode_disabled_dont_override_global_sql_mode run_without_connection do |orig_connection| - ActiveRecord::Model.establish_connection(orig_connection.merge({:strict => false})) - global_sql_mode = ActiveRecord::Model.connection.exec_query "SELECT @@GLOBAL.sql_mode" - session_sql_mode = ActiveRecord::Model.connection.exec_query "SELECT @@SESSION.sql_mode" + ActiveRecord::Base.establish_connection(orig_connection.merge({:strict => false})) + global_sql_mode = ActiveRecord::Base.connection.exec_query "SELECT @@GLOBAL.sql_mode" + session_sql_mode = ActiveRecord::Base.connection.exec_query "SELECT @@SESSION.sql_mode" assert_equal global_sql_mode.rows, session_sql_mode.rows end end @@ -76,11 +76,11 @@ class MysqlConnectionTest < ActiveRecord::TestCase private def run_without_connection - original_connection = ActiveRecord::Model.remove_connection + original_connection = ActiveRecord::Base.remove_connection begin yield original_connection ensure - ActiveRecord::Model.establish_connection(original_connection) + ActiveRecord::Base.establish_connection(original_connection) end end end diff --git a/activerecord/test/cases/adapters/mysql2/sql_types_test.rb b/activerecord/test/cases/adapters/mysql2/sql_types_test.rb new file mode 100644 index 0000000000..1ddb1b91c9 --- /dev/null +++ b/activerecord/test/cases/adapters/mysql2/sql_types_test.rb @@ -0,0 +1,14 @@ +require "cases/helper" + +class SqlTypesTest < ActiveRecord::TestCase + def test_binary_types + assert_equal 'varbinary(64)', type_to_sql(:binary, 64) + assert_equal 'varbinary(4095)', type_to_sql(:binary, 4095) + assert_equal 'blob(4096)', type_to_sql(:binary, 4096) + assert_equal 'blob', type_to_sql(:binary) + end + + def type_to_sql(*args) + ActiveRecord::Base.connection.type_to_sql(*args) + end +end diff --git a/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb b/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb index 74288a98d1..d03d1dd94c 100644 --- a/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb +++ b/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb @@ -32,6 +32,11 @@ class CopyTableTest < ActiveRecord::TestCase end end + def test_copy_table_allows_to_pass_options_to_create_table + @connection.create_table('blocker_table') + test_copy_table('customers', 'blocker_table', force: true) + end + def test_copy_table_with_index test_copy_table('comments', 'comments_with_index') do @connection.add_index('comments_with_index', ['post_id', 'type']) @@ -43,7 +48,9 @@ class CopyTableTest < ActiveRecord::TestCase end def test_copy_table_without_primary_key - test_copy_table('developers_projects', 'programmers_projects') + test_copy_table('developers_projects', 'programmers_projects') do + assert_nil @connection.primary_key('programmers_projects') + end end def test_copy_table_with_id_col_that_is_not_primary_key diff --git a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb index 4e26c5dda1..003052bac4 100644 --- a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb +++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb @@ -154,6 +154,12 @@ module ActiveRecord DualEncoding.connection.drop_table('dual_encodings') end + def test_type_cast_should_not_mutate_encoding + name = 'hello'.force_encoding(Encoding::ASCII_8BIT) + Owner.create(name: name) + assert_equal Encoding::ASCII_8BIT, name.encoding + end + def test_execute @conn.execute "INSERT INTO items (number) VALUES (10)" records = @conn.execute "SELECT * FROM items" diff --git a/activerecord/test/cases/associations/inverse_associations_test.rb b/activerecord/test/cases/associations/inverse_associations_test.rb index aad48e7ce9..8c9b4fb921 100644 --- a/activerecord/test/cases/associations/inverse_associations_test.rb +++ b/activerecord/test/cases/associations/inverse_associations_test.rb @@ -261,10 +261,23 @@ class InverseHasManyTests < ActiveRecord::TestCase def test_parent_instance_should_be_shared_with_first_and_last_child man = Man.first + assert man.interests.first.man.equal? man assert man.interests.last.man.equal? man end + def test_parent_instance_should_be_shared_with_first_n_and_last_n_children + man = Man.first + + interests = man.interests.first(2) + assert interests[0].man.equal? man + assert interests[1].man.equal? man + + interests = man.interests.last(2) + assert interests[0].man.equal? man + assert interests[1].man.equal? man + end + def test_trying_to_use_inverses_that_dont_exist_should_raise_an_error assert_raise(ActiveRecord::InverseOfAssociationNotFoundError) { Man.first.secret_interests } end diff --git a/activerecord/test/cases/attribute_methods/read_test.rb b/activerecord/test/cases/attribute_methods/read_test.rb index da5d9d8c2a..8d8ff2f952 100644 --- a/activerecord/test/cases/attribute_methods/read_test.rb +++ b/activerecord/test/cases/attribute_methods/read_test.rb @@ -11,10 +11,8 @@ module ActiveRecord def setup @klass = Class.new do def self.superclass; Base; end - def self.active_record_super; Base; end def self.base_class; self; end - extend ActiveRecord::Configuration include ActiveRecord::AttributeMethods def self.define_attribute_methods diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index c2b58fd7d1..8b82b79219 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -287,6 +287,12 @@ class AttributeMethodsTest < ActiveRecord::TestCase assert_equal "Don't change the topic", topic[:title] end + def test_read_attribute_raises_missing_attribute_error_when_not_exists + computer = Computer.select('id').first + assert_raises(ActiveModel::MissingAttributeError) { computer[:developer] } + assert_raises(ActiveModel::MissingAttributeError) { computer[:extendedWarranty] } + end + def test_read_attribute_when_false topic = topics(:first) topic.approved = false diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 0f859bf452..203e44857a 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -23,7 +23,6 @@ require 'models/edge' require 'models/joke' require 'models/bulb' require 'models/bird' -require 'models/teapot' require 'rexml/document' require 'active_support/core_ext/exception' @@ -1196,10 +1195,6 @@ class BasicsTest < ActiveRecord::TestCase # Concrete subclasses an abstract class which has a type column. assert !SubStiPost.descends_from_active_record? - - assert Teapot.descends_from_active_record? - assert !OtherTeapot.descends_from_active_record? - assert CoolTeapot.descends_from_active_record? end def test_find_on_abstract_base_class_doesnt_use_type_condition @@ -1453,15 +1448,6 @@ class BasicsTest < ActiveRecord::TestCase assert_equal scope, Bird.uniq end - def test_active_record_super - assert_equal ActiveRecord::Model, ActiveRecord::Base.active_record_super - assert_equal ActiveRecord::Base, Topic.active_record_super - assert_equal Topic, ImportantTopic.active_record_super - assert_equal ActiveRecord::Model, Teapot.active_record_super - assert_equal Teapot, OtherTeapot.active_record_super - assert_equal ActiveRecord::Model, CoolTeapot.active_record_super - end - def test_table_name_with_2_abstract_subclasses assert_equal "photos", Photo.table_name end diff --git a/activerecord/test/cases/batches_test.rb b/activerecord/test/cases/batches_test.rb index 12d5245fbd..acb8b5f562 100644 --- a/activerecord/test/cases/batches_test.rb +++ b/activerecord/test/cases/batches_test.rb @@ -136,4 +136,12 @@ class EachTest < ActiveRecord::TestCase assert_equal nick_order_subscribers[1..-1].map(&:id), subscribers.map(&:id) end + + def test_find_in_batches_should_use_any_column_as_primary_key_when_start_is_not_specified + assert_queries(Subscriber.count + 1) do + Subscriber.find_each(:batch_size => 1) do |subscriber| + assert_kind_of Subscriber, subscriber + end + end + end end diff --git a/activerecord/test/cases/column_test.rb b/activerecord/test/cases/column_test.rb index a7b63d15c9..2124c256fa 100644 --- a/activerecord/test/cases/column_test.rb +++ b/activerecord/test/cases/column_test.rb @@ -33,6 +33,8 @@ module ActiveRecord assert_equal 0, column.type_cast('bad1') assert_equal 0, column.type_cast('bad') assert_equal 1, column.type_cast(1.7) + assert_equal 0, column.type_cast(false) + assert_equal 1, column.type_cast(true) assert_nil column.type_cast(nil) end @@ -41,11 +43,9 @@ module ActiveRecord assert_raises(NoMethodError) do column.type_cast([]) end + assert_raises(NoMethodError) do - column.type_cast(true) - end - assert_raises(NoMethodError) do - column.type_cast(false) + column.type_cast(Object.new) end end diff --git a/activerecord/test/cases/connection_adapters/connection_handler_test.rb b/activerecord/test/cases/connection_adapters/connection_handler_test.rb index 4467ddfc39..631bf1aaac 100644 --- a/activerecord/test/cases/connection_adapters/connection_handler_test.rb +++ b/activerecord/test/cases/connection_adapters/connection_handler_test.rb @@ -4,8 +4,8 @@ module ActiveRecord module ConnectionAdapters class ConnectionHandlerTest < ActiveRecord::TestCase def setup - @klass = Class.new { include ActiveRecord::Tag } - @subklass = Class.new(@klass) { include ActiveRecord::Tag } + @klass = Class.new(Base) + @subklass = Class.new(@klass) @handler = ConnectionHandler.new @handler.establish_connection @klass, Base.connection_pool.spec diff --git a/activerecord/test/cases/connection_specification/resolver_test.rb b/activerecord/test/cases/connection_specification/resolver_test.rb index 434d2b7ba5..ee9818678d 100644 --- a/activerecord/test/cases/connection_specification/resolver_test.rb +++ b/activerecord/test/cases/connection_specification/resolver_test.rb @@ -9,7 +9,7 @@ module ActiveRecord end def test_url_host_no_db - skip "only if mysql is available" unless defined?(MysqlAdapter) + skip "only if mysql is available" unless current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter) spec = resolve 'mysql://foo?encoding=utf8' assert_equal({ :adapter => "mysql", @@ -18,7 +18,7 @@ module ActiveRecord end def test_url_host_db - skip "only if mysql is available" unless defined?(MysqlAdapter) + skip "only if mysql is available" unless current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter) spec = resolve 'mysql://foo/bar?encoding=utf8' assert_equal({ :adapter => "mysql", @@ -28,7 +28,7 @@ module ActiveRecord end def test_url_port - skip "only if mysql is available" unless defined?(MysqlAdapter) + skip "only if mysql is available" unless current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter) spec = resolve 'mysql://foo:123?encoding=utf8' assert_equal({ :adapter => "mysql", @@ -36,6 +36,14 @@ module ActiveRecord :host => "foo", :encoding => "utf8" }, spec) end + + def test_encoded_password + skip "only if mysql is available" unless current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter) + password = 'am@z1ng_p@ssw0rd#!' + encoded_password = URI.encode_www_form_component(password) + spec = resolve "mysql://foo:#{encoded_password}@localhost/bar" + assert_equal password, spec[:password] + end end end end diff --git a/activerecord/test/cases/defaults_test.rb b/activerecord/test/cases/defaults_test.rb index 0df872ff10..ed7eedaa27 100644 --- a/activerecord/test/cases/defaults_test.rb +++ b/activerecord/test/cases/defaults_test.rb @@ -52,12 +52,12 @@ if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter) self.use_transactional_fixtures = false def using_strict(strict) - connection = ActiveRecord::Model.remove_connection - ActiveRecord::Model.establish_connection connection.merge(strict: strict) + connection = ActiveRecord::Base.remove_connection + ActiveRecord::Base.establish_connection connection.merge(strict: strict) yield ensure - ActiveRecord::Model.remove_connection - ActiveRecord::Model.establish_connection connection + ActiveRecord::Base.remove_connection + ActiveRecord::Base.establish_connection connection end # MySQL cannot have defaults on text/blob columns. It reports the diff --git a/activerecord/test/cases/inclusion_test.rb b/activerecord/test/cases/inclusion_test.rb deleted file mode 100644 index 8f095e4953..0000000000 --- a/activerecord/test/cases/inclusion_test.rb +++ /dev/null @@ -1,133 +0,0 @@ -require 'cases/helper' -require 'models/teapot' - -class BasicInclusionModelTest < ActiveRecord::TestCase - def test_basic_model - Teapot.create!(:name => "Ronnie Kemper") - assert_equal "Ronnie Kemper", Teapot.first.name - end - - def test_initialization - t = Teapot.new(:name => "Bob") - assert_equal "Bob", t.name - end - - def test_inherited_model - teapot = CoolTeapot.create!(:name => "Bob") - teapot.reload - - assert_equal "Bob", teapot.name - assert_equal "mmm", teapot.aaahhh - end - - def test_generated_feature_methods - assert Teapot < Teapot::GeneratedFeatureMethods - end - - def test_exists - t = Teapot.create!(:name => "Ronnie Kemper") - assert Teapot.exists?(t) - end - - def test_predicate_builder - t = Teapot.create!(:name => "Bob") - assert_equal "Bob", Teapot.where(:id => [t]).first.name - assert_equal "Bob", Teapot.where(:id => t).first.name - end - - def test_nested_model - assert_equal "ceiling_teapots", Ceiling::Teapot.table_name - end -end - -class InclusionUnitTest < ActiveRecord::TestCase - def setup - @klass = Class.new { include ActiveRecord::Model } - end - - def test_non_abstract_class - assert !@klass.abstract_class? - end - - def test_abstract_class - @klass.abstract_class = true - assert @klass.abstract_class? - end - - def test_establish_connection - assert @klass.respond_to?(:establish_connection) - assert ActiveRecord::Model.respond_to?(:establish_connection) - end - - def test_adapter_connection - name = "#{ActiveRecord::Base.connection_config[:adapter]}_connection" - assert @klass.respond_to?(name) - assert ActiveRecord::Model.respond_to?(name) - end - - def test_connection_handler - assert_equal ActiveRecord::Base.connection_handler, @klass.connection_handler - end - - def test_mirrored_configuration - ActiveRecord::Base.time_zone_aware_attributes = true - assert @klass.time_zone_aware_attributes - ActiveRecord::Base.time_zone_aware_attributes = false - assert !@klass.time_zone_aware_attributes - ensure - ActiveRecord::Base.time_zone_aware_attributes = false - end - - # Doesn't really test anything, but this is here to ensure warnings don't occur - def test_included_twice - @klass.send :include, ActiveRecord::Model - end - - def test_deprecation_proxy - proxy = ActiveRecord::Model::DeprecationProxy.new - - assert_equal ActiveRecord::Model.name, proxy.name - assert_equal ActiveRecord::Base.superclass, assert_deprecated { proxy.superclass } - - sup, sup2 = nil, nil - ActiveSupport.on_load(:__test_active_record_model_deprecation) do - sup = superclass - sup2 = send(:superclass) - end - assert_deprecated do - ActiveSupport.run_load_hooks(:__test_active_record_model_deprecation, proxy) - end - assert_equal ActiveRecord::Base.superclass, sup - assert_equal ActiveRecord::Base.superclass, sup2 - end - - test "including in deprecation proxy" do - model, base = ActiveRecord::Model.dup, ActiveRecord::Base.dup - proxy = ActiveRecord::Model::DeprecationProxy.new(model, base) - - mod = Module.new - proxy.include mod - assert model < mod - end - - test "extending in deprecation proxy" do - model, base = ActiveRecord::Model.dup, ActiveRecord::Base.dup - proxy = ActiveRecord::Model::DeprecationProxy.new(model, base) - - mod = Module.new - assert_deprecated { proxy.extend mod } - assert base.singleton_class < mod - end -end - -class InclusionFixturesTest < ActiveRecord::TestCase - fixtures :teapots - - def test_fixtured_record - assert_equal "Bob", teapots(:bob).name - end - - def test_timestamped_fixture - assert_not_nil teapots(:bob).created_at - end -end diff --git a/activerecord/test/cases/inheritance_test.rb b/activerecord/test/cases/inheritance_test.rb index 8fded9159f..aab7aa51dd 100644 --- a/activerecord/test/cases/inheritance_test.rb +++ b/activerecord/test/cases/inheritance_test.rb @@ -4,7 +4,6 @@ require 'models/person' require 'models/post' require 'models/project' require 'models/subscriber' -require 'models/teapot' require 'models/vegetables' class InheritanceTest < ActiveRecord::TestCase @@ -81,10 +80,6 @@ class InheritanceTest < ActiveRecord::TestCase assert_equal SubStiPost, SubStiPost.base_class end - def test_active_record_model_included_base_class - assert_equal Teapot, Teapot.base_class - end - def test_abstract_inheritance_base_class assert_equal LoosePerson, LoosePerson.base_class assert_equal LooseDescendant, LooseDescendant.base_class @@ -93,11 +88,7 @@ class InheritanceTest < ActiveRecord::TestCase end def test_base_class_activerecord_error - klass = Class.new { - extend ActiveRecord::Configuration - include ActiveRecord::Inheritance - } - + klass = Class.new { include ActiveRecord::Inheritance } assert_raise(ActiveRecord::ActiveRecordError) { klass.base_class } end diff --git a/activerecord/test/cases/migration/change_schema_test.rb b/activerecord/test/cases/migration/change_schema_test.rb index 17c1634444..86451289e7 100644 --- a/activerecord/test/cases/migration/change_schema_test.rb +++ b/activerecord/test/cases/migration/change_schema_test.rb @@ -132,6 +132,26 @@ module ActiveRecord assert_equal %w(foo testingid), connection.columns(:testings).map(&:name).sort end + def test_create_table_raises_when_redefining_primary_key_column + error = assert_raise(ArgumentError) do + connection.create_table :testings do |t| + t.column :id, :string + end + end + + assert_equal "you can't redefine the primary key column 'id'. To define a custom primary key, pass { id: false } to create_table.", error.message + end + + def test_create_table_raises_when_redefining_custom_primary_key_column + error = assert_raise(ArgumentError) do + connection.create_table :testings, primary_key: :testing_id do |t| + t.column :testing_id, :string + end + end + + assert_equal "you can't redefine the primary key column 'testing_id'. To define a custom primary key, pass { id: false } to create_table.", error.message + end + def test_create_table_with_timestamps_should_create_datetime_columns connection.create_table table_name do |t| t.timestamps diff --git a/activerecord/test/cases/multiple_db_test.rb b/activerecord/test/cases/multiple_db_test.rb index 42461e8ecb..2e386a172a 100644 --- a/activerecord/test/cases/multiple_db_test.rb +++ b/activerecord/test/cases/multiple_db_test.rb @@ -96,12 +96,12 @@ class MultipleDbTest < ActiveRecord::TestCase unless in_memory_db? def test_associations_should_work_when_model_has_no_connection begin - ActiveRecord::Model.remove_connection + ActiveRecord::Base.remove_connection assert_nothing_raised ActiveRecord::ConnectionNotEstablished do College.first.courses.first end ensure - ActiveRecord::Model.establish_connection 'arunit' + ActiveRecord::Base.establish_connection 'arunit' end end end diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb index b5f32a57b2..4b938da5c4 100644 --- a/activerecord/test/cases/persistence_test.rb +++ b/activerecord/test/cases/persistence_test.rb @@ -592,6 +592,19 @@ class PersistencesTest < ActiveRecord::TestCase assert_equal 'super_title', t.title end + def test_update_columns_changing_id + topic = Topic.find(1) + topic.update_columns(id: 123) + assert_equal 123, topic.id + topic.reload + assert_equal 123, topic.id + end + + def test_update_columns_returns_boolean + topic = Topic.find(1) + assert_equal true, topic.update_columns(title: "New title") + end + def test_update_attributes topic = Topic.find(1) assert !topic.approved? diff --git a/activerecord/test/cases/pooled_connections_test.rb b/activerecord/test/cases/pooled_connections_test.rb index 0a6354f5cc..a8a9b06ec4 100644 --- a/activerecord/test/cases/pooled_connections_test.rb +++ b/activerecord/test/cases/pooled_connections_test.rb @@ -7,17 +7,17 @@ class PooledConnectionsTest < ActiveRecord::TestCase def setup @per_test_teardown = [] - @connection = ActiveRecord::Model.remove_connection + @connection = ActiveRecord::Base.remove_connection end def teardown - ActiveRecord::Model.clear_all_connections! - ActiveRecord::Model.establish_connection(@connection) + ActiveRecord::Base.clear_all_connections! + ActiveRecord::Base.establish_connection(@connection) @per_test_teardown.each {|td| td.call } end def checkout_connections - ActiveRecord::Model.establish_connection(@connection.merge({:pool => 2, :checkout_timeout => 0.3})) + ActiveRecord::Base.establish_connection(@connection.merge({:pool => 2, :checkout_timeout => 0.3})) @connections = [] @timed_out = 0 @@ -34,15 +34,15 @@ class PooledConnectionsTest < ActiveRecord::TestCase # Will deadlock due to lack of Monitor timeouts in 1.9 def checkout_checkin_connections(pool_size, threads) - ActiveRecord::Model.establish_connection(@connection.merge({:pool => pool_size, :checkout_timeout => 0.5})) + ActiveRecord::Base.establish_connection(@connection.merge({:pool => pool_size, :checkout_timeout => 0.5})) @connection_count = 0 @timed_out = 0 threads.times do Thread.new do begin - conn = ActiveRecord::Model.connection_pool.checkout + conn = ActiveRecord::Base.connection_pool.checkout sleep 0.1 - ActiveRecord::Model.connection_pool.checkin conn + ActiveRecord::Base.connection_pool.checkin conn @connection_count += 1 rescue ActiveRecord::ConnectionTimeoutError @timed_out += 1 @@ -55,13 +55,13 @@ class PooledConnectionsTest < ActiveRecord::TestCase checkout_checkin_connections 1, 2 assert_equal 2, @connection_count assert_equal 0, @timed_out - assert_equal 1, ActiveRecord::Model.connection_pool.connections.size + assert_equal 1, ActiveRecord::Base.connection_pool.connections.size end private def add_record(name) - ActiveRecord::Model.connection_pool.with_connection { Project.create! :name => name } + ActiveRecord::Base.connection_pool.with_connection { Project.create! :name => name } end end unless current_adapter?(:FrontBase) || in_memory_db? diff --git a/activerecord/test/cases/primary_keys_test.rb b/activerecord/test/cases/primary_keys_test.rb index e6e50a4cd4..bf8aacc363 100644 --- a/activerecord/test/cases/primary_keys_test.rb +++ b/activerecord/test/cases/primary_keys_test.rb @@ -188,14 +188,14 @@ class PrimaryKeyWithNoConnectionTest < ActiveRecord::TestCase def test_set_primary_key_with_no_connection return skip("disconnect wipes in-memory db") if in_memory_db? - connection = ActiveRecord::Model.remove_connection + connection = ActiveRecord::Base.remove_connection model = Class.new(ActiveRecord::Base) model.primary_key = 'foo' assert_equal 'foo', model.primary_key - ActiveRecord::Model.establish_connection(connection) + ActiveRecord::Base.establish_connection(connection) assert_equal 'foo', model.primary_key end diff --git a/activerecord/test/cases/query_cache_test.rb b/activerecord/test/cases/query_cache_test.rb index 51a285a2b4..4ff481e6a5 100644 --- a/activerecord/test/cases/query_cache_test.rb +++ b/activerecord/test/cases/query_cache_test.rb @@ -184,6 +184,17 @@ class QueryCacheTest < ActiveRecord::TestCase assert_queries(2) { task.lock!; task.lock! } end end + + def test_cache_is_available_when_connection_is_connected + conf = ActiveRecord::Base.configurations + + ActiveRecord::Base.configurations = {} + Task.cache do + assert_queries(1) { Task.find(1); Task.find(1) } + end + ensure + ActiveRecord::Base.configurations = conf + end end class QueryCacheExpiryTest < ActiveRecord::TestCase diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb index 6399111be6..98e278df82 100644 --- a/activerecord/test/cases/relation_test.rb +++ b/activerecord/test/cases/relation_test.rb @@ -256,5 +256,11 @@ module ActiveRecord test 'merge with a proc' do assert_equal [:foo], relation.merge(-> { where(:foo) }).where_values end + + test 'none!' do + assert relation.none!.equal?(relation) + assert_equal [NullRelation], relation.extending_values + assert relation.is_a?(NullRelation) + end end end diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 5f96145b47..bc6cac0c6c 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -1391,6 +1391,15 @@ class RelationTest < ActiveRecord::TestCase end end + test "loaded relations cannot be mutated by extending!" do + relation = Post.all + relation.to_a + + assert_raises(ActiveRecord::ImmutableRelation) do + relation.extending! Module.new + end + end + test "relations show the records in #inspect" do relation = Post.limit(2) assert_equal "#<ActiveRecord::Relation [#{Post.limit(2).map(&:inspect).join(', ')}]>", relation.inspect diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb index 5f13124e5b..7ff0044bd4 100644 --- a/activerecord/test/cases/schema_dumper_test.rb +++ b/activerecord/test/cases/schema_dumper_test.rb @@ -203,6 +203,12 @@ class SchemaDumperTest < ActiveRecord::TestCase assert_match %r{t.text\s+"body",\s+null: false$}, output end + def test_schema_dump_includes_length_for_mysql_binary_fields + output = standard_dump + assert_match %r{t.binary\s+"var_binary",\s+limit: 255$}, output + assert_match %r{t.binary\s+"var_binary_large",\s+limit: 4095$}, output + end + def test_schema_dump_includes_length_for_mysql_blob_and_text_fields output = standard_dump assert_match %r{t.binary\s+"tiny_blob",\s+limit: 255$}, output diff --git a/activerecord/test/cases/serialization_test.rb b/activerecord/test/cases/serialization_test.rb index 25b860878a..eb9cb91e32 100644 --- a/activerecord/test/cases/serialization_test.rb +++ b/activerecord/test/cases/serialization_test.rb @@ -45,4 +45,20 @@ class SerializationTest < ActiveRecord::TestCase assert_equal @contact_attributes[:awesome], contact.awesome, "For #{format}" end end + + def test_include_root_in_json_allows_inheritance + original_root_in_json = ActiveRecord::Base.include_root_in_json + ActiveRecord::Base.include_root_in_json = true + + klazz = Class.new(ActiveRecord::Base) + klazz.table_name = 'topics' + assert klazz.include_root_in_json + + klazz.include_root_in_json = false + assert ActiveRecord::Base.include_root_in_json + assert !klazz.include_root_in_json + assert !klazz.new.include_root_in_json + ensure + ActiveRecord::Base.include_root_in_json = original_root_in_json + end end diff --git a/activerecord/test/cases/serialized_attribute_test.rb b/activerecord/test/cases/serialized_attribute_test.rb index f24ee54cd2..068f3cf3cd 100644 --- a/activerecord/test/cases/serialized_attribute_test.rb +++ b/activerecord/test/cases/serialized_attribute_test.rb @@ -56,11 +56,21 @@ class SerializedAttributeTest < ActiveRecord::TestCase def test_serialized_attribute_before_type_cast_returns_unserialized_value Topic.serialize :content, Hash - t = Topic.new(:content => { :foo => :bar }) - assert_equal({ :foo => :bar }, t.content_before_type_cast) + t = Topic.new(content: { foo: :bar }) + assert_equal({ foo: :bar }, t.content_before_type_cast) t.save! t.reload - assert_equal({ :foo => :bar }, t.content_before_type_cast) + assert_equal({ foo: :bar }, t.content_before_type_cast) + end + + def test_serialized_attributes_before_type_cast_returns_unserialized_value + Topic.serialize :content, Hash + + t = Topic.new(content: { foo: :bar }) + assert_equal({ foo: :bar }, t.attributes_before_type_cast["content"]) + t.save! + t.reload + assert_equal({ foo: :bar }, t.attributes_before_type_cast["content"]) end def test_serialized_attribute_calling_dup_method diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb index bb4f2c8064..fdca10f4fb 100644 --- a/activerecord/test/cases/transactions_test.rb +++ b/activerecord/test/cases/transactions_test.rb @@ -14,6 +14,21 @@ class TransactionTest < ActiveRecord::TestCase @first, @second = Topic.find(1, 2).sort_by { |t| t.id } end + def test_raise_after_destroy + refute @first.frozen? + + assert_raises(RuntimeError) { + Topic.transaction do + @first.destroy + assert @first.frozen? + raise + end + } + + assert @first.reload + refute @first.frozen? + end + def test_successful Topic.transaction do @first.approved = true diff --git a/activerecord/test/cases/unconnected_test.rb b/activerecord/test/cases/unconnected_test.rb index 5a69054445..e82ca3f93d 100644 --- a/activerecord/test/cases/unconnected_test.rb +++ b/activerecord/test/cases/unconnected_test.rb @@ -7,13 +7,13 @@ class TestUnconnectedAdapter < ActiveRecord::TestCase self.use_transactional_fixtures = false def setup - @underlying = ActiveRecord::Model.connection - @specification = ActiveRecord::Model.remove_connection + @underlying = ActiveRecord::Base.connection + @specification = ActiveRecord::Base.remove_connection end def teardown @underlying = nil - ActiveRecord::Model.establish_connection(@specification) + ActiveRecord::Base.establish_connection(@specification) load_schema if in_memory_db? end diff --git a/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb b/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb index 174d96aa4e..32d2bf746f 100644 --- a/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb +++ b/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb @@ -59,4 +59,18 @@ class I18nGenerateMessageValidationTest < ActiveRecord::TestCase assert_equal "Custom taken message", @topic.errors.generate_message(:title, :taken, :value => 'title') end + test "translation for 'taken' can be overridden in activerecord scope" do + I18n.backend.store_translations "en", {activerecord: {errors: {messages: {taken: "Custom taken message" }}}} + assert_equal "Custom taken message", @topic.errors.generate_message(:title, :taken, :value => 'title') + end + + test "translation for 'taken' can be overridden in activerecord model scope" do + I18n.backend.store_translations "en", {activerecord: {errors: {models: {topic: {taken: "Custom taken message" }}}}} + assert_equal "Custom taken message", @topic.errors.generate_message(:title, :taken, :value => 'title') + end + + test "translation for 'taken' can be overridden in activerecord attributes scope" do + I18n.backend.store_translations "en", {activerecord: {errors: {models: {topic: {attributes: {title: {taken: "Custom taken message" }}}}}}} + assert_equal "Custom taken message", @topic.errors.generate_message(:title, :taken, :value => 'title') + end end diff --git a/activerecord/test/models/teapot.rb b/activerecord/test/models/teapot.rb deleted file mode 100644 index b035b18c1b..0000000000 --- a/activerecord/test/models/teapot.rb +++ /dev/null @@ -1,35 +0,0 @@ -class Teapot - # I'm a little teapot, - # Short and stout, - # Here is my handle - # Here is my spout - # When I get all steamed up, - # Hear me shout, - # Tip me over and pour me out! - # - # HELL YEAH TEAPOT SONG - - include ActiveRecord::Model -end - -class OtherTeapot < Teapot -end - -class OMFGIMATEAPOT - def aaahhh - "mmm" - end -end - -class CoolTeapot < OMFGIMATEAPOT - include ActiveRecord::Model - self.table_name = "teapots" -end - -class Ceiling - include ActiveRecord::Model - - class Teapot - include ActiveRecord::Model - end -end diff --git a/activerecord/test/schema/mysql2_specific_schema.rb b/activerecord/test/schema/mysql2_specific_schema.rb index 24a43d7ece..f25f72c481 100644 --- a/activerecord/test/schema/mysql2_specific_schema.rb +++ b/activerecord/test/schema/mysql2_specific_schema.rb @@ -1,15 +1,19 @@ ActiveRecord::Schema.define do - create_table :binary_fields, :force => true do |t| - t.binary :tiny_blob, :limit => 255 - t.binary :normal_blob, :limit => 65535 - t.binary :medium_blob, :limit => 16777215 - t.binary :long_blob, :limit => 2147483647 - t.text :tiny_text, :limit => 255 - t.text :normal_text, :limit => 65535 - t.text :medium_text, :limit => 16777215 - t.text :long_text, :limit => 2147483647 + create_table :binary_fields, force: true do |t| + t.binary :var_binary, limit: 255 + t.binary :var_binary_large, limit: 4095 + t.column :tiny_blob, 'tinyblob', limit: 255 + t.binary :normal_blob, limit: 65535 + t.binary :medium_blob, limit: 16777215 + t.binary :long_blob, limit: 2147483647 + t.text :tiny_text, limit: 255 + t.text :normal_text, limit: 65535 + t.text :medium_text, limit: 16777215 + t.text :long_text, limit: 2147483647 end + add_index :binary_fields, :var_binary + ActiveRecord::Base.connection.execute <<-SQL DROP PROCEDURE IF EXISTS ten; SQL diff --git a/activerecord/test/schema/mysql_specific_schema.rb b/activerecord/test/schema/mysql_specific_schema.rb index 802c08b819..5401c12ed5 100644 --- a/activerecord/test/schema/mysql_specific_schema.rb +++ b/activerecord/test/schema/mysql_specific_schema.rb @@ -1,15 +1,19 @@ ActiveRecord::Schema.define do - create_table :binary_fields, :force => true do |t| - t.binary :tiny_blob, :limit => 255 - t.binary :normal_blob, :limit => 65535 - t.binary :medium_blob, :limit => 16777215 - t.binary :long_blob, :limit => 2147483647 - t.text :tiny_text, :limit => 255 - t.text :normal_text, :limit => 65535 - t.text :medium_text, :limit => 16777215 - t.text :long_text, :limit => 2147483647 + create_table :binary_fields, force: true do |t| + t.binary :var_binary, limit: 255 + t.binary :var_binary_large, limit: 4095 + t.column :tiny_blob, 'tinyblob', limit: 255 + t.binary :normal_blob, limit: 65535 + t.binary :medium_blob, limit: 16777215 + t.binary :long_blob, limit: 2147483647 + t.text :tiny_text, limit: 255 + t.text :normal_text, limit: 65535 + t.text :medium_text, limit: 16777215 + t.text :long_text, limit: 2147483647 end + add_index :binary_fields, :var_binary + ActiveRecord::Base.connection.execute <<-SQL DROP PROCEDURE IF EXISTS ten; SQL diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index 2e4ec96933..eec06754a5 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -648,12 +648,6 @@ ActiveRecord::Schema.define do t.datetime :ending end - create_table :teapots, :force => true do |t| - t.string :name - t.string :type - t.timestamps - end - create_table :topics, :force => true do |t| t.string :title t.string :author_name diff --git a/activerecord/test/support/connection.rb b/activerecord/test/support/connection.rb index bea894a583..196b3a9493 100644 --- a/activerecord/test/support/connection.rb +++ b/activerecord/test/support/connection.rb @@ -13,9 +13,9 @@ module ARTest def self.connect puts "Using #{connection_name}" - ActiveRecord::Model.logger = ActiveSupport::Logger.new("debug.log", 0, 100 * 1024 * 1024) - ActiveRecord::Model.configurations = connection_config - ActiveRecord::Model.establish_connection 'arunit' + ActiveRecord::Base.logger = ActiveSupport::Logger.new("debug.log", 0, 100 * 1024 * 1024) + ActiveRecord::Base.configurations = connection_config + ActiveRecord::Base.establish_connection 'arunit' ARUnit2Model.establish_connection 'arunit2' end end diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 23e2ce0b03..6534c0af85 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,5 +1,16 @@ ## Rails 4.0.0 (unreleased) ## +* Fixed timezone mapping of the Solomon Islands. *Steve Klabnik* + +* Make callstack attribute optional in + ActiveSupport::Deprecation::Reporting methods `warn` and `deprecation_warning` + + *Alexey Gaziev* + +* Implement HashWithIndifferentAccess#replace so key? works correctly. *David Graham* + +* Handle the possible Permission Denied errors atomic.rb might trigger due to its chown and chmod calls. *Daniele Sluijters* + * Hash#extract! returns only those keys that present in the receiver. {:a => 1, :b => 2}.extract!(:a, :x) # => {:a => 1} diff --git a/activesupport/lib/active_support/benchmarkable.rb b/activesupport/lib/active_support/benchmarkable.rb index 3d8bb13c49..6413502b53 100644 --- a/activesupport/lib/active_support/benchmarkable.rb +++ b/activesupport/lib/active_support/benchmarkable.rb @@ -48,7 +48,8 @@ module ActiveSupport # Silence the logger during the execution of the block. def silence - ActiveSupport::Deprecation.warn "ActiveSupport::Benchmarkable#silence is deprecated. It will be removed from Rails 4.1." + message = "ActiveSupport::Benchmarkable#silence is deprecated. It will be removed from Rails 4.1." + ActiveSupport::Deprecation.warn message old_logger_level, logger.level = logger.level, ::Logger::ERROR if logger yield ensure diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb index 732ab4b7df..9a53870b3d 100644 --- a/activesupport/lib/active_support/cache.rb +++ b/activesupport/lib/active_support/cache.rb @@ -182,10 +182,10 @@ module ActiveSupport # the cache with the given key, then that data is returned. # # If there is no such data in the cache (a cache miss), then +nil+ will be - # returned. However, if a block has been passed, that block will be run - # in the event of a cache miss. The return value of the block will be - # written to the cache under the given cache key, and that return value - # will be returned. + # returned. However, if a block has been passed, that block will be passed + # the key and executed in the event of a cache miss. The return value of the + # block will be written to the cache under the given cache key, and that + # return value will be returned. # # cache.write('today', 'Monday') # cache.fetch('today') # => "Monday" @@ -300,7 +300,7 @@ module ActiveSupport entry.value else result = instrument(:generate, name, options) do |payload| - yield + yield(name) end write(name, result, options) result diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb index a02793bde9..8199f431f1 100644 --- a/activesupport/lib/active_support/callbacks.rb +++ b/activesupport/lib/active_support/callbacks.rb @@ -282,12 +282,15 @@ module ActiveSupport def _normalize_legacy_filter(kind, filter) if !filter.respond_to?(kind) && filter.respond_to?(:filter) - ActiveSupport::Deprecation.warn("Filter object with #filter method is deprecated. Define method corresponding to filter type (#before, #after or #around).") + message = "Filter object with #filter method is deprecated. Define method corresponding " \ + "to filter type (#before, #after or #around)." + ActiveSupport::Deprecation.warn message filter.singleton_class.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 def #{kind}(context, &block) filter(context, &block) end RUBY_EVAL elsif filter.respond_to?(:before) && filter.respond_to?(:after) && kind == :around && !filter.respond_to?(:around) - ActiveSupport::Deprecation.warn("Filter object with #before and #after methods is deprecated. Define #around method instead.") + message = "Filter object with #before and #after methods is deprecated. Define #around method instead." + ActiveSupport::Deprecation.warn message def filter.around(context) should_continue = before(context) yield if should_continue diff --git a/activesupport/lib/active_support/core_ext/array/uniq_by.rb b/activesupport/lib/active_support/core_ext/array/uniq_by.rb index c1d5a355a4..ca3b7748cd 100644 --- a/activesupport/lib/active_support/core_ext/array/uniq_by.rb +++ b/activesupport/lib/active_support/core_ext/array/uniq_by.rb @@ -5,7 +5,7 @@ class Array # # [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', caller + ActiveSupport::Deprecation.warn 'uniq_by is deprecated. Use Array#uniq instead' uniq(&block) end @@ -13,7 +13,7 @@ class Array # # Same as +uniq_by+, but modifies +self+. def uniq_by!(&block) - ActiveSupport::Deprecation.warn 'uniq_by! is deprecated. Use Array#uniq! instead', caller + 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_time/calculations.rb b/activesupport/lib/active_support/core_ext/date_time/calculations.rb index 385aa586bb..0c6437b02b 100644 --- a/activesupport/lib/active_support/core_ext/date_time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date_time/calculations.rb @@ -4,7 +4,7 @@ class DateTime class << self # *DEPRECATED*: Use +DateTime.civil_from_format+ directly. def local_offset - ActiveSupport::Deprecation.warn 'DateTime.local_offset is deprecated. Use DateTime.civil_from_format directly.', caller + ActiveSupport::Deprecation.warn 'DateTime.local_offset is deprecated. Use DateTime.civil_from_format directly.' ::Time.local(2012).utc_offset.to_r / 86400 end diff --git a/activesupport/lib/active_support/core_ext/file/atomic.rb b/activesupport/lib/active_support/core_ext/file/atomic.rb index 81beb4e85d..c3e6124a57 100644 --- a/activesupport/lib/active_support/core_ext/file/atomic.rb +++ b/activesupport/lib/active_support/core_ext/file/atomic.rb @@ -23,10 +23,10 @@ class File yield temp_file temp_file.close - begin + if File.exists?(file_name) # Get original file permissions old_stat = stat(file_name) - rescue Errno::ENOENT + else # If not possible, probe which are the default permissions in the # destination directory. old_stat = probe_stat_in(dirname(file_name)) @@ -36,8 +36,13 @@ class File FileUtils.mv(temp_file.path, file_name) # Set correct permissions on new file - chown(old_stat.uid, old_stat.gid, file_name) - chmod(old_stat.mode, file_name) + begin + chown(old_stat.uid, old_stat.gid, file_name) + # This operation will affect filesystem ACL's + chmod(old_stat.mode, file_name) + rescue Errno::EPERM + # Changing file ownership failed, moving on. + end end # Private utility method. diff --git a/activesupport/lib/active_support/core_ext/module/introspection.rb b/activesupport/lib/active_support/core_ext/module/introspection.rb index 649a969149..08e5f8a5c3 100644 --- a/activesupport/lib/active_support/core_ext/module/introspection.rb +++ b/activesupport/lib/active_support/core_ext/module/introspection.rb @@ -72,7 +72,7 @@ class Module # This method is useful for forward compatibility, since Ruby 1.8 returns # constant names as strings, whereas 1.9 returns them as symbols. def local_constant_names - ActiveSupport::Deprecation.warn 'Module#local_constant_names is deprecated, use Module#local_constants instead', caller + ActiveSupport::Deprecation.warn 'Module#local_constant_names is deprecated, use Module#local_constants instead' local_constants.map { |c| c.to_s } end end diff --git a/activesupport/lib/active_support/core_ext/proc.rb b/activesupport/lib/active_support/core_ext/proc.rb index cd63740940..166c3855a0 100644 --- a/activesupport/lib/active_support/core_ext/proc.rb +++ b/activesupport/lib/active_support/core_ext/proc.rb @@ -3,7 +3,7 @@ require "active_support/deprecation" class Proc #:nodoc: def bind(object) - ActiveSupport::Deprecation.warn 'Proc#bind is deprecated and will be removed in future versions', caller + ActiveSupport::Deprecation.warn 'Proc#bind is deprecated and will be removed in future versions' block, time = self, Time.now object.class_eval do diff --git a/activesupport/lib/active_support/core_ext/string/encoding.rb b/activesupport/lib/active_support/core_ext/string/encoding.rb index dc635ed6a5..a583b914db 100644 --- a/activesupport/lib/active_support/core_ext/string/encoding.rb +++ b/activesupport/lib/active_support/core_ext/string/encoding.rb @@ -2,7 +2,7 @@ require 'active_support/deprecation' class String def encoding_aware? - ActiveSupport::Deprecation.warn 'String#encoding_aware? is deprecated', caller + ActiveSupport::Deprecation.warn 'String#encoding_aware? is deprecated' true end end diff --git a/activesupport/lib/active_support/deprecation/method_wrappers.rb b/activesupport/lib/active_support/deprecation/method_wrappers.rb index d3907b03e5..cab8a1b14d 100644 --- a/activesupport/lib/active_support/deprecation/method_wrappers.rb +++ b/activesupport/lib/active_support/deprecation/method_wrappers.rb @@ -33,7 +33,7 @@ module ActiveSupport method_names.each do |method_name| target_module.alias_method_chain(method_name, :deprecation) do |target, punctuation| target_module.send(:define_method, "#{target}_with_deprecation#{punctuation}") do |*args, &block| - deprecator.deprecation_warning(method_name, options[method_name], caller) + deprecator.deprecation_warning(method_name, options[method_name]) send(:"#{target}_without_deprecation#{punctuation}", *args, &block) end end diff --git a/activesupport/lib/active_support/deprecation/reporting.rb b/activesupport/lib/active_support/deprecation/reporting.rb index 1ce54d9381..a7d265d732 100644 --- a/activesupport/lib/active_support/deprecation/reporting.rb +++ b/activesupport/lib/active_support/deprecation/reporting.rb @@ -11,8 +11,10 @@ module ActiveSupport # # ActiveSupport::Deprecation.warn('something broke!') # # => "DEPRECATION WARNING: something broke! (called from your_code.rb:1)" - def warn(message = nil, callstack = caller) + def warn(message = nil, callstack = nil) return if silenced + + callstack ||= caller(2) deprecation_message(callstack, message).tap do |m| behavior.each { |b| b.call(m, callstack) } end @@ -34,7 +36,8 @@ module ActiveSupport @silenced = old_silenced end - def deprecation_warning(deprecated_method_name, message = nil, caller_backtrace = caller) + def deprecation_warning(deprecated_method_name, message = nil, caller_backtrace = nil) + caller_backtrace ||= caller(2) deprecated_method_warning(deprecated_method_name, message).tap do |msg| warn(msg, caller_backtrace) end diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb index 0c78f1611f..306d80b2df 100644 --- a/activesupport/lib/active_support/hash_with_indifferent_access.rb +++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb @@ -204,6 +204,14 @@ module ActiveSupport replace(reverse_merge( other_hash )) end + # Replaces the contents of this hash with other_hash. + # + # h = { "a" => 100, "b" => 200 } + # h.replace({ "c" => 300, "d" => 400 }) #=> {"c"=>300, "d"=>400} + def replace(other_hash) + super(self.class.new_from_hash_copying_default(other_hash)) + end + # Removes the specified key from the hash. def delete(key) super(convert_key(key)) diff --git a/activesupport/lib/active_support/json/variable.rb b/activesupport/lib/active_support/json/variable.rb index 8af661a795..d69dab6408 100644 --- a/activesupport/lib/active_support/json/variable.rb +++ b/activesupport/lib/active_support/json/variable.rb @@ -5,8 +5,9 @@ module ActiveSupport # Deprecated: A string that returns itself as its JSON-encoded form. class Variable < String def initialize(*args) - ActiveSupport::Deprecation.warn 'ActiveSupport::JSON::Variable is deprecated and will be removed in Rails 4.1. ' \ - 'For your own custom JSON literals, define #as_json and #encode_json yourself.' + message = 'ActiveSupport::JSON::Variable is deprecated and will be removed in Rails 4.1. ' \ + 'For your own custom JSON literals, define #as_json and #encode_json yourself.' + ActiveSupport::Deprecation.warn message super end diff --git a/activesupport/lib/active_support/queueing.rb b/activesupport/lib/active_support/queueing.rb index c8ba28021d..a89a48d057 100644 --- a/activesupport/lib/active_support/queueing.rb +++ b/activesupport/lib/active_support/queueing.rb @@ -64,9 +64,12 @@ module ActiveSupport # queue and joins the thread, which will ensure that all jobs # are executed before the process finally dies. class ThreadedQueueConsumer + attr_accessor :logger + def initialize(queue, options = {}) @queue = queue @logger = options[:logger] + @fallback_logger = Logger.new($stderr) end def start @@ -80,7 +83,7 @@ module ActiveSupport end def drain - run(@queue.pop) until @queue.empty? + @queue.pop.run until @queue.empty? end def consume @@ -96,8 +99,7 @@ module ActiveSupport end def handle_exception(job, exception) - raise exception unless @logger - @logger.error "Job Error: #{exception.message}\n#{exception.backtrace.join("\n")}" + (logger || @fallback_logger).error "Job Error: #{job.inspect}\n#{exception.message}\n#{exception.backtrace.join("\n")}" end end end diff --git a/activesupport/lib/active_support/test_case.rb b/activesupport/lib/active_support/test_case.rb index 7b9378a7f6..8b06739b7f 100644 --- a/activesupport/lib/active_support/test_case.rb +++ b/activesupport/lib/active_support/test_case.rb @@ -17,7 +17,7 @@ module ActiveSupport # Use AS::TestCase for the base class when describing a model register_spec_type(self) do |desc| - Class === desc && desc < ActiveRecord::Model + Class === desc && desc < ActiveRecord::Base end Assertion = MiniTest::Assertion @@ -43,7 +43,8 @@ module ActiveSupport if block_given? super else - ActiveSupport::Deprecation.warn("`describe` without a block is deprecated, please switch to: `def self.name; #{text.inspect}; end`\n") + message = "`describe` without a block is deprecated, please switch to: `def self.name; #{text.inspect}; end`\n" + ActiveSupport::Deprecation.warn message class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 def self.name diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb index 231d61da96..0207f53238 100644 --- a/activesupport/lib/active_support/values/time_zone.rb +++ b/activesupport/lib/active_support/values/time_zone.rb @@ -167,7 +167,7 @@ module ActiveSupport "Guam" => "Pacific/Guam", "Port Moresby" => "Pacific/Port_Moresby", "Magadan" => "Asia/Magadan", - "Solomon Is." => "Asia/Magadan", + "Solomon Is." => "Pacific/Guadalcanal", "New Caledonia" => "Pacific/Noumea", "Fiji" => "Pacific/Fiji", "Kamchatka" => "Asia/Kamchatka", diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb index 9f76f4c90b..ed903746c8 100644 --- a/activesupport/test/caching_test.rb +++ b/activesupport/test/caching_test.rb @@ -83,7 +83,7 @@ class CacheKeyTest < ActiveSupport::TestCase def test_expand_cache_key_of_true assert_equal 'true', ActiveSupport::Cache.expand_cache_key(true) end - + def test_expand_cache_key_of_array_like_object assert_equal 'foo/bar/baz', ActiveSupport::Cache.expand_cache_key(%w{foo bar baz}.to_enum) end @@ -197,6 +197,16 @@ module CacheStoreBehavior assert_equal 'baz', @cache.fetch('foo') { 'baz' } end + def test_fetch_with_cache_miss_passes_key_to_block + cache_miss = false + assert_equal 3, @cache.fetch('foo') { |key| cache_miss = true; key.length } + assert cache_miss + + cache_miss = false + assert_equal 3, @cache.fetch('foo') { |key| cache_miss = true; key.length } + assert !cache_miss + end + def test_fetch_with_forced_cache_miss @cache.write('foo', 'bar') @cache.expects(:read).never @@ -594,7 +604,7 @@ class FileStoreTest < ActiveSupport::TestCase assert_equal "views/index?id=1", @cache_with_pathname.send(:file_path_key, key) end - # Test that generated cache keys are short enough to have Tempfile stuff added to them and + # Test that generated cache keys are short enough to have Tempfile stuff added to them and # remain valid def test_filename_max_size key = "#{'A' * ActiveSupport::Cache::FileStore::FILENAME_MAX_SIZE}" diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb index 7cfe7b0ea7..6746b58cd3 100644 --- a/activesupport/test/core_ext/hash_ext_test.rb +++ b/activesupport/test/core_ext/hash_ext_test.rb @@ -428,6 +428,18 @@ class HashExtTest < ActiveSupport::TestCase assert_equal 2, hash['b'] end + def test_indifferent_replace + hash = HashWithIndifferentAccess.new + hash[:a] = 42 + + replaced = hash.replace(b: 12) + + assert hash.key?('b') + assert !hash.key?(:a) + assert_equal 12, hash[:b] + assert_same hash, replaced + end + def test_indifferent_merging_with_block hash = HashWithIndifferentAccess.new hash[:a] = 1 diff --git a/activesupport/test/core_ext/module/qualified_const_test.rb b/activesupport/test/core_ext/module/qualified_const_test.rb index 343a848a42..37c9228a64 100644 --- a/activesupport/test/core_ext/module/qualified_const_test.rb +++ b/activesupport/test/core_ext/module/qualified_const_test.rb @@ -89,13 +89,20 @@ class QualifiedConstTest < ActiveSupport::TestCase end test "reject absolute paths" do - assert_raise(NameError, "wrong constant name ::X") { Object.qualified_const_defined?("::X")} - assert_raise(NameError, "wrong constant name ::X") { Object.qualified_const_defined?("::X::Y")} + assert_raise_with_message(NameError, "wrong constant name ::X") { Object.qualified_const_defined?("::X")} + assert_raise_with_message(NameError, "wrong constant name ::X") { Object.qualified_const_defined?("::X::Y")} - assert_raise(NameError, "wrong constant name ::X") { Object.qualified_const_get("::X")} - assert_raise(NameError, "wrong constant name ::X") { Object.qualified_const_get("::X::Y")} + assert_raise_with_message(NameError, "wrong constant name ::X") { Object.qualified_const_get("::X")} + assert_raise_with_message(NameError, "wrong constant name ::X") { Object.qualified_const_get("::X::Y")} - assert_raise(NameError, "wrong constant name ::X") { Object.qualified_const_set("::X", nil)} - assert_raise(NameError, "wrong constant name ::X") { Object.qualified_const_set("::X::Y", nil)} + assert_raise_with_message(NameError, "wrong constant name ::X") { Object.qualified_const_set("::X", nil)} + assert_raise_with_message(NameError, "wrong constant name ::X") { Object.qualified_const_set("::X::Y", nil)} + end + + private + + def assert_raise_with_message(expected_exception, expected_message, &block) + exception = assert_raise(expected_exception, &block) + assert_equal expected_message, exception.message end end diff --git a/activesupport/test/dependencies_test.rb b/activesupport/test/dependencies_test.rb index e5bc806397..670a04e5df 100644 --- a/activesupport/test/dependencies_test.rb +++ b/activesupport/test/dependencies_test.rb @@ -147,7 +147,8 @@ class DependenciesTest < ActiveSupport::TestCase def test_circular_autoloading_detection with_autoloading_fixtures do - assert_raise(RuntimeError, "Circular dependency detected while autoloading constant Circular1") { Circular1 } + e = assert_raise(RuntimeError) { Circular1 } + assert_equal "Circular dependency detected while autoloading constant Circular1", e.message end end diff --git a/activesupport/test/deprecation_test.rb b/activesupport/test/deprecation_test.rb index c081103cc7..332100f5a1 100644 --- a/activesupport/test/deprecation_test.rb +++ b/activesupport/test/deprecation_test.rb @@ -9,7 +9,7 @@ class Deprecatee def old_request; @request end def partially(foo = nil) - ActiveSupport::Deprecation.warn('calling with foo=nil is out', caller) if foo.nil? + ActiveSupport::Deprecation.warn('calling with foo=nil is out') if foo.nil? end def not() 2 end diff --git a/activesupport/test/queueing/threaded_consumer_test.rb b/activesupport/test/queueing/threaded_consumer_test.rb index fc43cb555a..a3ca46a261 100644 --- a/activesupport/test/queueing/threaded_consumer_test.rb +++ b/activesupport/test/queueing/threaded_consumer_test.rb @@ -65,10 +65,22 @@ class TestThreadConsumer < ActiveSupport::TestCase job = Job.new { raise "RuntimeError: Error!" } @queue.push job - @queue.drain + consume_queue @queue assert_equal 1, @logger.logged(:error).size - assert_match 'Job Error: RuntimeError: Error!', @logger.logged(:error).last + assert_match "Job Error: #{job.inspect}\nRuntimeError: Error!", @logger.logged(:error).last + end + + test "logger defaults to stderr" do + begin + $stderr, old_stderr = StringIO.new, $stderr + queue = ActiveSupport::Queue.new + queue.push Job.new { raise "RuntimeError: Error!" } + consume_queue queue + assert_match 'Job Error', $stderr.string + ensure + $stderr = old_stderr + end end test "test overriding exception handling" do @@ -85,8 +97,14 @@ class TestThreadConsumer < ActiveSupport::TestCase job = Job.new { raise "RuntimeError: Error!" } @queue.push job - @queue.drain + consume_queue @queue assert_equal "RuntimeError: Error!", @queue.consumer.last_error end + + private + def consume_queue(queue) + queue.push nil + queue.consumer.consume + end end diff --git a/guides/CHANGELOG.md b/guides/CHANGELOG.md index a1a480e911..11fe1c5efa 100644 --- a/guides/CHANGELOG.md +++ b/guides/CHANGELOG.md @@ -1,5 +1,7 @@ ## Rails 4.0.0 (unreleased) ## +* New guide _Working with JavaScript in Rails_. *Steve Klabnik* + * Guides updated to reflect new test locations. *Mike Moore* * Guides have a responsive design. *Joe Fiorini* diff --git a/guides/source/4_0_release_notes.md b/guides/source/4_0_release_notes.md index 6b3680aa2d..f24a981c6e 100644 --- a/guides/source/4_0_release_notes.md +++ b/guides/source/4_0_release_notes.md @@ -642,14 +642,6 @@ Active Record * Connections *must* be closed at the end of a thread. If not, your connection pool can fill and an exception will be raised. -* Added the `ActiveRecord::Model` module which can be included in a class as an alternative to inheriting from `ActiveRecord::Base`: - - ```ruby - class Post - include ActiveRecord::Model - end - ``` - * PostgreSQL hstore records can be created. * PostgreSQL hstore types are automatically deserialized from the database. diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md index e7ed62ef47..4b2cc947b5 100644 --- a/guides/source/active_support_core_extensions.md +++ b/guides/source/active_support_core_extensions.md @@ -3716,7 +3716,9 @@ File.atomic_write(joined_asset_path) do |cache| end ``` -To accomplish this `atomic_write` creates a temporary file. That's the file the code in the block actually writes to. On completion, the temporary file is renamed, which is an atomic operation on POSIX systems. If the target file exists `atomic_write` overwrites it and keeps owners and permissions. +To accomplish this `atomic_write` creates a temporary file. That's the file the code in the block actually writes to. On completion, the temporary file is renamed, which is an atomic operation on POSIX systems. If the target file exists `atomic_write` overwrites it and keeps owners and permissions. However there are a few cases where `atomic_write` cannot change the file ownership or permissions, this error is caught and skipped over trusting in the user/filesystem to ensure the file is accessible to the processes that need it. + +NOTE. Due to the chmod operation `atomic_write` performs, if the target file has an ACL set on it this ACL will be recalculated/modified. WARNING. Note you can't append with `atomic_write`. diff --git a/guides/source/documents.yaml b/guides/source/documents.yaml index 0b22423798..19425765b8 100644 --- a/guides/source/documents.yaml +++ b/guides/source/documents.yaml @@ -1,4 +1,4 @@ -- +- name: Start Here documents: - @@ -26,82 +26,87 @@ description: This guide covers the database query interface provided by Active Record. - name: Views - documents: - - + documents: + - name: Layouts and Rendering in Rails url: layouts_and_rendering.html description: This guide covers the basic layout features of Action Controller and Action View, including rendering and redirecting, using content_for blocks, and working with partials. - - + - name: Action View Form Helpers url: form_helpers.html description: Guide to using built-in Form helpers. - name: Controllers documents: - - + - name: Action Controller Overview url: action_controller_overview.html description: This guide covers how controllers work and how they fit into the request cycle in your application. It includes sessions, filters, and cookies, data streaming, and dealing with exceptions raised by a request, among other topics. - - + - name: Rails Routing from the Outside In url: routing.html description: This guide covers the user-facing features of Rails routing. If you want to understand how to use routing in your own Rails applications, start here. - name: Digging Deeper documents: - - + - name: Active Support Core Extensions url: active_support_core_extensions.html description: This guide documents the Ruby core extensions defined in Active Support. - - + - name: Rails Internationalization API url: i18n.html description: This guide covers how to add internationalization to your applications. Your application will be able to translate content to different languages, change pluralization rules, use correct date formats for each country and so on. - - + - name: Action Mailer Basics url: action_mailer_basics.html work_in_progress: true description: This guide describes how to use Action Mailer to send and receive emails. - - + - name: Testing Rails Applications url: testing.html work_in_progress: true description: This is a rather comprehensive guide to doing both unit and functional tests in Rails. It covers everything from 'What is a test?' to the testing APIs. Enjoy. - - + - name: Securing Rails Applications url: security.html description: This guide describes common security problems in web applications and how to avoid them with Rails. - - + - name: Debugging Rails Applications url: debugging_rails_applications.html description: This guide describes how to debug Rails applications. It covers the different ways of achieving this and how to understand what is happening "behind the scenes" of your code. - - + - name: Performance Testing Rails Applications url: performance_testing.html description: This guide covers the various ways of performance testing a Ruby on Rails application. - - + - name: Configuring Rails Applications url: configuring.html description: This guide covers the basic configuration settings for a Rails application. - - + - name: Rails Command Line Tools and Rake Tasks url: command_line.html description: This guide covers the command line tools and rake tasks provided by Rails. - - + - name: Caching with Rails work_in_progress: true url: caching_with_rails.html description: Various caching techniques provided by Rails. - - + - name: Asset Pipeline url: asset_pipeline.html description: This guide documents the asset pipeline. - - + - + name: Working with JavaScript in Rails + work_in_progress: true + url: working_with_javascript_in_rails.html + description: This guide covers the built-in Ajax/JavaScript functionality of Rails. + - name: Getting Started with Engines url: engines.html description: This guide explains how to write a mountable engine. work_in_progress: true - - + - name: The Rails Initialization Process work_in_progress: true url: initialization.html @@ -109,38 +114,38 @@ - name: Extending Rails documents: - - + - name: The Basics of Creating Rails Plugins work_in_progress: true url: plugins.html description: This guide covers how to build a plugin to extend the functionality of Rails. - - + - name: Rails on Rack url: rails_on_rack.html description: This guide covers Rails integration with Rack and interfacing with other Rack components. - - + - name: Creating and Customizing Rails Generators url: generators.html description: This guide covers the process of adding a brand new generator to your extension or providing an alternative to an element of a built-in Rails generator (such as providing alternative test stubs for the scaffold generator). - name: Contributing to Ruby on Rails documents: - - + - name: Contributing to Ruby on Rails url: contributing_to_ruby_on_rails.html description: Rails is not 'somebody else's framework.' This guide covers a variety of ways that you can get involved in the ongoing development of Rails. - - + - name: API Documentation Guidelines url: api_documentation_guidelines.html description: This guide documents the Ruby on Rails API documentation guidelines. - - + - name: Ruby on Rails Guides Guidelines url: ruby_on_rails_guides_guidelines.html description: This guide documents the Ruby on Rails guides guidelines. - name: Release Notes documents: - - + - name: Upgrading Ruby on Rails url: upgrading_ruby_on_rails.html work_in_progress: true @@ -157,15 +162,15 @@ name: Ruby on Rails 3.1 Release Notes url: 3_1_release_notes.html description: Release notes for Rails 3.1. - - + - name: Ruby on Rails 3.0 Release Notes url: 3_0_release_notes.html description: Release notes for Rails 3.0. - - + - name: Ruby on Rails 2.3 Release Notes url: 2_3_release_notes.html description: Release notes for Rails 2.3. - - + - name: Ruby on Rails 2.2 Release Notes url: 2_2_release_notes.html description: Release notes for Rails 2.2. diff --git a/guides/source/form_helpers.md b/guides/source/form_helpers.md index ec1f1d4df1..f5db76f217 100644 --- a/guides/source/form_helpers.md +++ b/guides/source/form_helpers.md @@ -900,7 +900,7 @@ end } ``` -The keys of the `:addresses_attributes` hash are unimportant, they need merely be different for each address. +The keys of the `:addresses_attributes` hash are unimportant, they need merely be different for each address. If the associated object is already saved, `fields_for` autogenerates a hidden input with the `id` of the saved record. You can disable this by passing `:include_id => false` to `fields_for`. You may wish to do this if the autogenerated input is placed in a location where an input tag is not valid HTML or when using an ORM where children do not have an id. diff --git a/guides/source/working_with_javascript.md b/guides/source/working_with_javascript_in_rails.md index 106744d605..011303c311 100644 --- a/guides/source/working_with_javascript.md +++ b/guides/source/working_with_javascript_in_rails.md @@ -1,22 +1,22 @@ -Working With JavaScript -======================= +Working with JavaScript in Rails +================================ This guide covers the built-in Ajax/JavaScript functionality of Rails (and -more); it will enable you to create rich and dynamic AJAX applications with +more); it will enable you to create rich and dynamic Ajax applications with ease! We will cover the following topics: -* Quick introduction to AJAX +* Quick introduction to Ajax * Unobtrusive JavaScript * How Rails' built-in helpers assist you -* Handling AJAX on the server side +* Handling Ajax on the server side * The Turbolinks gem ------------------------------------------------------------------------------- -An introduction to AJAX +An Introduction to Ajax ------------------------ -In order to understand AJAX, you must first understand what a web browser does +In order to understand Ajax, you must first understand what a web browser does normally. When you type `http://localhost:3000` into your browser's address bar and hit @@ -30,16 +30,16 @@ JavaScript can also make requests to the server, and parse the response. It also has the ability to update information on the page. Combining these two powers, a JavaScript writer can make a web page that can update just parts of itself, without needing to get the full page data from the server. This is a -powerful technique that we call AJAX. +powerful technique that we call Ajax. Rails ships with CoffeeScript by default, and so the rest of the examples in this guide will be in CoffeeScript. All of these lessons, of course, apply to vanilla JavaScript as well. -As an example, here's some CoffeeScript code that makes an AJAX request using +As an example, here's some CoffeeScript code that makes an Ajax request using the jQuery library: -``` +```coffeescript $.ajax(url: "/test").done (html) -> $("#results").append html ``` @@ -63,35 +63,35 @@ demonstrate other ways. Here's the simplest way to write JavaScript. You may see it referred to as 'inline JavaScript': -``` +```html <a href="#" onclick="alert('Hello, world.')">Here</a> ``` When clicked, the alert will trigger. Here's the problem: what happens when we have lots of JavaScript we want to execute on a click? -``` +```html <a href="#" onclick="function fib(n){return n<2?n:fib(n-1)+fib(n-2);};alert('fib of 15 is: ' + fib(15) + '.');">Calculate</a> ``` Awkward, right? We could pull the function definition out of the click handler, and turn it into CoffeeScript: -``` +```coffeescript fib = (n) -> (if n < 2 then n else fib(n - 1) + fib(n - 2)) ``` And then on our page: -``` +```html <a href="#" onclick="alert('fib of 15 is: ' + fib(15) + '.');">Calculate</a> ``` That's a little bit better, but what about multiple links that have the same effect? -``` +```html <a href="#" onclick="alert('fib of 16 is: ' + fib(16) + '.');">Calculate</a> <a href="#" onclick="alert('fib of 17 is: ' + fib(17) + '.');">Calculate</a> <a href="#" onclick="alert('fib of 18 is: ' + fib(18) + '.');">Calculate</a> @@ -101,7 +101,7 @@ Not very DRY, eh? We can fix this by using events instead. We'll add a `data-*` attribute to our link, and then bind a handler to the click event of every link that has that attribute: -``` +```coffeescript fib = (n) -> (if n < 2 then n else fib(n - 1) + fib(n - 2)) @@ -133,11 +133,12 @@ Built-in Helpers ---------------------- Rails provides a bunch of view helper methods written in Ruby to assist you -in generating HTML. Sometimes, you want to add a little AJAX to those elements, +in generating HTML. Sometimes, you want to add a little Ajax to those elements, and Rails has got your back in those cases. -Because of Unobtrusive JavaScript, the Rails AJAX helpers actually consist of two -parts: the JavaScript part and the Ruby part. +Because of Unobtrusive JavaScript, the Rails "Ajax helpers" are actually in two +parts: the JavaScript half and the Ruby half. + [rails.js](https://github.com/rails/jquery-ujs/blob/master/src/rails.js) provides the JavaScript half, and the regular Ruby view helpers add appropriate tags to your DOM. The CoffeeScript in rails.js then listens for these @@ -149,7 +150,7 @@ attributes, and attaches appropriate handlers. is a helper that assists with writing forms. `form_for` takes a `:remote` option. It works like this: -``` +```erb <%= form_for(@post, remote: true) do |f| %> ... <% end %> @@ -157,27 +158,25 @@ option. It works like this: This will generate the following HTML: -``` +```html <form accept-charset="UTF-8" action="/posts" class="new_post" data-remote="true" id="new_post" method="post"> ... </form> ``` -Note the `data-remote='true'`. Now, the form will be submitted by AJAX rather +Note the `data-remote='true'`. Now, the form will be submitted by Ajax rather than by the browser's normal submit mechanism. You probably don't want to just sit there with a filled out `<form>`, though. You probably want to do something upon a successful submission. To do that, bind to the `ajax:success` event. On failure, use `ajax:error`. Check it out: -``` -<script> +```coffeescript $(document).ready -> $("#new_post").on("ajax:success", (e, data, status, xhr) -> $("#new_post").append xhr.responseText ).bind "ajax:error", (e, xhr, status, error) -> $("#new_post").append "<p>ERROR</p>" -</script> ``` Obviously, you'll want to be a bit more sophisticated than that, but it's a @@ -189,7 +188,7 @@ start. is very similar to `form_for`. It has a `:remote` option that you can use like this: -``` +```erb <%= form_tag('/posts', remote: true) %> ``` @@ -199,60 +198,59 @@ details. ### link_to [`link_to`](http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-link_to) -is a helper that assits with generating links. It has a `:remote` option you +is a helper that assists with generating links. It has a `:remote` option you can use like this: -``` +```erb <%= link_to "first post", @post, remote: true %> ``` which generates -``` +```html <a href="/posts/1" data-remote="true">a post</a> ``` -You can bind to the same AJAX events as `form_for`. Here's an example. Let's +You can bind to the same Ajax events as `form_for`. Here's an example. Let's assume that we have a resource `/fib/:n` that calculates the `n`th Fibonacci number. We would generate some HTML like this: -``` +```erb <%= link_to "Calculate", "/fib/15", remote: true, data: { fib: 15 } %> ``` and write some CoffeeScript like this: -``` +```coffeescript $(document).ready -> $("a[data-fib]").on "ajax:success", (e, data, status, xhr) -> count = $(this).data("fib") alert "fib of #{count} is: #{data}." ``` -Easy! - ### button_to [`button_to`](http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-button_to) is a helper that helps you create buttons. It has a `:remote` option that you can call like this: -``` +```erb <%= button_to "A post", @post, remote: true %> ``` this generates -``` +```html <form action="/posts/1" class="button_to" data-remote="true" method="post"> + <div><input type="submit" value="A post"></div> </form> ``` Since it's just a `<form>`, all of the information on `form_for` also applies. -Server side concerns +Server-Side Concerns -------------------- -AJAX isn't just client-side, you also need to do some work on the server -side to support it. Often, people like their AJAX requests to return JSON +Ajax isn't just client-side, you also need to do some work on the server +side to support it. Often, people like their Ajax requests to return JSON rather than HTML. Let's discuss what it takes to make that happen. ### A Simple Example @@ -261,7 +259,7 @@ Imagine you have a series of users that you would like to display and provide a form on that same page to create a new user. The index action of your controller looks like this: -``` +```ruby class UsersController < ApplicationController def index @users = User.all @@ -272,7 +270,7 @@ class UsersController < ApplicationController The index view (`app/views/users/index.html.erb`) contains: -``` +```erb <b>Users</b> <ul id="users"> @@ -292,7 +290,7 @@ The index view (`app/views/users/index.html.erb`) contains: The `app/views/users/_user.html.erb` partial contains the following: -``` +```erb <li><%= user.name %></li> ``` @@ -301,11 +299,11 @@ provides a form to create a new user. The bottom form will call the create action on the Users controller. Because the form's remote option is set to true, the request will be posted to the -users controller as an AJAX request, looking for JavaScript. In order to +users controller as an Ajax request, looking for JavaScript. In order to service that request, the create action of your controller would look like this: -``` +```ruby # app/controllers/users_controller.rb # ...... def create @@ -325,11 +323,11 @@ this: ``` Notice the format.js in the respond_to block; that allows the controller to -respond to your AJAX request. You then have a corresponding +respond to your Ajax request. You then have a corresponding `app/views/users/create.js.erb` view file that generates the actual JavaScript code that will be sent and executed on the client side. -``` +```erb $("<%= escape_javascript(render @user) %>").appendTo("#users"); ``` @@ -337,14 +335,14 @@ Turbolinks ---------- Rails 4 ships with the [Turbolinks gem](https://github.com/rails/turbolinks). -This gem uses AJAX to speed up page rendering in most applications. +This gem uses Ajax to speed up page rendering in most applications. -### How Turbolinks works +### How Turbolinks Works Turbolinks attaches a click handler to all `<a>` on the page. If your browser supports [PushState](https://developer.mozilla.org/en-US/docs/DOM/Manipulating_the_browser_history#The_pushState(\).C2.A0method), -Turbolinks will make an AJAX request for the page, parse the response, and +Turbolinks will make an Ajax request for the page, parse the response, and replace the entire `<body>` of the page with the `<body>` of the response. It will then use PushState to change the URL to the correct one, preserving refresh semantics and giving you pretty URLs. @@ -356,16 +354,16 @@ and put `//= require turbolinks` in your CoffeeScript manifest, which is usually If you want to disable Turbolinks for certain links, add a `data-no-turbolink` attribute to the tag: -``` +```html <a href="..." data-no-turbolink>No turbolinks here</a>. ``` -### Page Change events +### Page Change Events When writing CoffeeScript, you'll often want to do some sort of processing upon page load. With jQuery, you'd write something like this: -``` +```coffeescript $(document).ready -> alert "page has loaded!" ``` @@ -374,7 +372,7 @@ However, because Turbolinks overrides the normal page loading process, the event that this relies on will not be fired. If you have code that looks like this, you must change your code to do this instead: -``` +```coffeescript $(document).on "page:change", -> alert "page has loaded!" ``` @@ -383,7 +381,7 @@ For more details, including other events you can bind to, check out [the Turbolinks README](https://github.com/rails/turbolinks/blob/ec9ca4d6cf9626e03a672f3b9e7968c816aff94e/README.md). -Other resources +Other Resources --------------- Here are some helpful links to help you learn even more: diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md index ba9b24c6c6..cc77d08684 100644 --- a/railties/CHANGELOG.md +++ b/railties/CHANGELOG.md @@ -1,5 +1,9 @@ ## Rails 4.0.0 (unreleased) ## +* Ensure that RAILS_ENV is set when accessing Rails.env *Steve Klabnik* + +* Don't eager-load app/assets and app/views *Elia Schito* + * Add `.rake` to list of file extensions included by `rake notes` and `rake notes:custom`. *Brent J. Nordquist* * New test locations `test/models`, `test/helpers`, `test/controllers`, and diff --git a/railties/lib/rails.rb b/railties/lib/rails.rb index dd51a6fb01..6bf2d8db20 100644 --- a/railties/lib/rails.rb +++ b/railties/lib/rails.rb @@ -85,7 +85,10 @@ module Rails end def env - @_env ||= ActiveSupport::StringInquirer.new(ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development") + @_env ||= begin + ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development" + ActiveSupport::StringInquirer.new(ENV["RAILS_ENV"]) + end end def env=(environment) diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb index 7df517de71..cc21213f1c 100644 --- a/railties/lib/rails/application/configuration.rb +++ b/railties/lib/rails/application/configuration.rb @@ -88,9 +88,10 @@ module Rails end def threadsafe! - ActiveSupport::Deprecation.warn "config.threadsafe! is deprecated. Rails applications " \ - "behave by default as thread safe in production as long as config.cache_classes and " \ - "config.eager_load are set to true" + message = "config.threadsafe! is deprecated. Rails applications " \ + "behave by default as thread safe in production as long as config.cache_classes and " \ + "config.eager_load are set to true" + ActiveSupport::Deprecation.warn message @cache_classes = true @eager_load = true self @@ -141,8 +142,7 @@ module Rails end def whiny_nils=(*) - ActiveSupport::Deprecation.warn "config.whiny_nils option " \ - "is deprecated and no longer works", caller + ActiveSupport::Deprecation.warn "config.whiny_nils option is deprecated and no longer works" end end end diff --git a/railties/lib/rails/application/finisher.rb b/railties/lib/rails/application/finisher.rb index c520f7af9d..2d87b8594a 100644 --- a/railties/lib/rails/application/finisher.rb +++ b/railties/lib/rails/application/finisher.rb @@ -99,6 +99,7 @@ module Rails initializer :activate_queue_consumer do |app| if config.queue.class == ActiveSupport::Queue app.queue_consumer = config.queue_consumer || config.queue.consumer + app.queue_consumer.logger ||= Rails.logger if app.queue_consumer.respond_to?(:logger=) app.queue_consumer.start at_exit { app.queue_consumer.shutdown } end diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb index e761e26b04..de3127f43e 100644 --- a/railties/lib/rails/generators/app_base.rb +++ b/railties/lib/rails/generators/app_base.rb @@ -209,7 +209,7 @@ module Rails # Gems used only for assets and not required # in production environments by default. group :assets do - gem 'sprockets-rails', github: 'rails/sprockets-rails' + gem 'sprockets-rails', '~> 2.0.0.rc1' gem 'sass-rails', '~> 4.0.0.beta' gem 'coffee-rails', '~> 4.0.0.beta' diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile b/railties/lib/rails/generators/rails/app/templates/Gemfile index 30f8a5f75e..5b7a653a09 100644 --- a/railties/lib/rails/generators/rails/app/templates/Gemfile +++ b/railties/lib/rails/generators/rails/app/templates/Gemfile @@ -7,7 +7,7 @@ source 'https://rubygems.org' <%= "gem 'jruby-openssl'\n" if defined?(JRUBY_VERSION) -%> <%= assets_gemfile_entry %> -<%= javascript_gemfile_entry %> +<%= javascript_gemfile_entry -%> # To use ActiveModel has_secure_password # gem 'bcrypt-ruby', '~> 3.0.0' diff --git a/railties/lib/rails/paths.rb b/railties/lib/rails/paths.rb index 8af4130e87..cfdb15a14e 100644 --- a/railties/lib/rails/paths.rb +++ b/railties/lib/rails/paths.rb @@ -99,14 +99,15 @@ module Rails protected def filter_by(constraint) - all = [] + yes = [] + no = [] + all_paths.each do |path| - if path.send(constraint) - paths = path.existent - paths -= path.children.map { |p| p.send(constraint) ? [] : p.existent }.flatten - all.concat(paths) - end + paths = path.existent + path.existent_base_paths + path.send(constraint) ? yes.concat(paths) : no.concat(paths) end + + all = yes - no all.uniq! all end @@ -134,6 +135,7 @@ module Rails keys.delete(@current) @root.values_at(*keys.sort) end + deprecate :children def first expanded.first @@ -210,6 +212,10 @@ module Rails expanded.select { |d| File.directory?(d) } end + def existent_base_paths + map { |p| File.expand_path(p, @root.path) }.select{ |f| File.exist? f } + end + alias to_a expanded end end diff --git a/railties/test/application/assets_test.rb b/railties/test/application/assets_test.rb index 1311194ea6..f86877db34 100644 --- a/railties/test/application/assets_test.rb +++ b/railties/test/application/assets_test.rb @@ -246,7 +246,7 @@ module ApplicationTests manifest = Dir["#{app_path}/public/assets/manifest-*.json"].first assets = ActiveSupport::JSON.decode(File.read(manifest)) - assert asset_path = assets["assets"].find { |(k, v)| k !~ /rails.png/ && k =~ /.png/ }[1] + assert asset_path = assets["assets"].find { |(k, _)| k !~ /rails.png/ && k =~ /.png/ }[1] require "#{app_path}/config/environment" diff --git a/railties/test/application/paths_test.rb b/railties/test/application/paths_test.rb index 4029984ce9..2265d220ab 100644 --- a/railties/test/application/paths_test.rb +++ b/railties/test/application/paths_test.rb @@ -59,6 +59,8 @@ module ApplicationTests assert eager_load.include?(root("app/controllers")) assert eager_load.include?(root("app/helpers")) assert eager_load.include?(root("app/models")) + assert !eager_load.include?(root("app/views")), "expected to not be in the eager_load_path" + assert !eager_load.include?(root("app/assets")), "expected to not be in the eager_load_path" end test "environments has a glob equal to the current environment" do @@ -73,11 +75,18 @@ module ApplicationTests assert_in_load_path "vendor" assert_not_in_load_path "app", "views" + assert_not_in_load_path "app", "assets" assert_not_in_load_path "config" assert_not_in_load_path "config", "locales" assert_not_in_load_path "config", "environments" assert_not_in_load_path "tmp" assert_not_in_load_path "tmp", "cache" end + + test "deprecated children method" do + assert_deprecated "children is deprecated and will be removed from Rails 4.1." do + @paths["app/assets"].children + end + end end end diff --git a/railties/test/application/queue_test.rb b/railties/test/application/queue_test.rb index b0b3cf18e9..219a35da35 100644 --- a/railties/test/application/queue_test.rb +++ b/railties/test/application/queue_test.rb @@ -78,6 +78,7 @@ module ApplicationTests assert_nil Rails.application.config.queue_consumer assert_kind_of ActiveSupport::ThreadedQueueConsumer, Rails.application.queue_consumer + assert_equal Rails.logger, Rails.application.queue_consumer.logger end test "attempting to marshal a queue will raise an exception" do diff --git a/railties/test/generators/task_generator_test.rb b/railties/test/generators/task_generator_test.rb index f810a21d1e..9399be9510 100644 --- a/railties/test/generators/task_generator_test.rb +++ b/railties/test/generators/task_generator_test.rb @@ -5,7 +5,7 @@ class TaskGeneratorTest < Rails::Generators::TestCase include GeneratorsTestHelper arguments %w(feeds foo bar) - def test_controller_skeleton_is_created + def test_task_is_created run_generator assert_file "lib/tasks/feeds.rake", /namespace :feeds/ end diff --git a/tools/profile b/tools/profile index d4cfa13f0a..665efe049d 100755 --- a/tools/profile +++ b/tools/profile @@ -1,12 +1,9 @@ #!/usr/bin/env ruby # Example: # tools/profile activesupport/lib/active_support.rb -abort 'Use REE so you can profile memory and object allocation' unless GC.respond_to?(:enable_stats) - ENV['NO_RELOAD'] ||= '1' ENV['RAILS_ENV'] ||= 'development' -GC.enable_stats Gem.source_index require 'benchmark' @@ -23,23 +20,18 @@ module RequireProfiler def profile(file) stats << [file, depth] self.depth += 1 - heap_before, objects_before = GC.allocated_size, ObjectSpace.allocated_objects result = nil elapsed = Benchmark.realtime { result = yield } - heap_after, objects_after = GC.allocated_size, ObjectSpace.allocated_objects self.depth -= 1 stats.pop if stats.last.first == file - stats << [file, depth, elapsed, heap_after - heap_before, objects_after - objects_before] if result + stats << [file, depth, elapsed] if result result end end end GC.start -before = GC.allocated_size -before_gctime, before_gcruns = GC.time, GC.collections before_rss = `ps -o rss= -p #{Process.pid}`.to_i -before_live_objects = ObjectSpace.live_objects path = ARGV.shift if mode = ARGV.shift @@ -53,12 +45,8 @@ end elapsed = Benchmark.realtime { require path } results = RubyProf.stop if mode -after_gctime, after_gcruns = GC.time, GC.collections GC.start -after_live_objects = ObjectSpace.live_objects after_rss = `ps -o rss= -p #{Process.pid}`.to_i -after = GC.allocated_size -usage = (after - before) / 1024.0 if mode if printer = ARGV.shift @@ -74,11 +62,11 @@ if mode end end -RequireProfiler.stats.each do |file, depth, sec, bytes, objects| +RequireProfiler.stats.each do |file, depth, sec| if sec - puts "%10.2f KB %10d obj %8.1f ms %s%s" % [bytes / 1024.0, objects, sec * 1000, ' ' * depth, file] + puts "%8.1f ms %s%s" % [sec * 1000, ' ' * depth, file] else - puts "#{' ' * (42 + depth)}#{file}" + puts "#{' ' * (13 + depth)}#{file}" end end -puts "%10.2f KB %10d obj %8.1f ms %d KB RSS %8.1f ms GC time %d GC runs" % [usage, after_live_objects - before_live_objects, elapsed * 1000, after_rss - before_rss, (after_gctime - before_gctime) / 1000.0, after_gcruns - before_gcruns] +puts "%8.1f ms %d KB RSS" % [elapsed * 1000, after_rss - before_rss] |