diff options
547 files changed, 4344 insertions, 3433 deletions
diff --git a/.rubocop.yml b/.rubocop.yml index e1102b45ce..629f5d86ea 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -6,6 +6,7 @@ AllCops: Exclude: - '**/templates/**/*' - '**/vendor/**/*' + - 'actionpack/lib/action_dispatch/journey/parser.rb' # Prefer &&/|| over and/or. Style/AndOr: diff --git a/.travis.yml b/.travis.yml index 86328e0ef6..0c59604ff4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,19 +34,19 @@ env: - "JRUBY_OPTS='--dev -J-Xmx1024M'" matrix: - "GEM=railties" - - "GEM=ap" - - "GEM=ac" - - "GEM=ac:integration" + - "GEM=ap,ac" - "GEM=am,amo,as,av,aj" - "GEM=as PRESERVE_TIMEZONES=1" - "GEM=ar:mysql2" - "GEM=ar:sqlite3" - "GEM=ar:postgresql" - "GEM=guides" + - "GEM=ac:integration" rvm: - 2.2.6 - 2.3.3 + - 2.4.0 - ruby-head matrix: @@ -74,17 +74,18 @@ matrix: - "GEM=ar:mysql2 MYSQL=mariadb" addons: mariadb: 10.0 - - rvm: jruby-9.1.5.0 + - rvm: jruby-9.1.6.0 jdk: oraclejdk8 env: - "GEM=ap" - - rvm: jruby-9.1.5.0 + - rvm: jruby-9.1.6.0 jdk: oraclejdk8 env: - "GEM=am,aj" allow_failures: - rvm: ruby-head - - rvm: jruby-9.1.5.0 + - rvm: jruby-9.1.6.0 + - env: "GEM=ac:integration" fast_finish: true notifications: @@ -7,6 +7,8 @@ end gemspec +gem "arel", github: "rails/arel" + # We need a newish Rake since Active Job sets its test tasks' descriptions. gem "rake", ">= 11.1" @@ -29,9 +31,6 @@ gem "bcrypt", "~> 3.1.11", require: false # sprockets. gem "uglifier", ">= 1.3.0", require: false -# Track stable branch of sass because it doesn't have circular require warnings. -gem "sass", github: "sass/sass", branch: "stable", require: false - # FIXME: Remove this fork after https://github.com/nex3/rb-inotify/pull/49 is fixed. gem "rb-inotify", github: "matthewd/rb-inotify", branch: "close-handling", require: false @@ -41,10 +40,10 @@ gem "json", ">= 2.0.0" gem "rubocop", require: false group :doc do - gem "sdoc", "1.0.0.beta2" + gem "sdoc", "1.0.0.rc1" gem "redcarpet", "~> 3.2.3", platforms: :ruby gem "w3c_validators" - gem "kindlerb", ">= 1.0.1" + gem "kindlerb", "~> 1.2.0" end # Active Support. diff --git a/Gemfile.lock b/Gemfile.lock index eb14330c2b..2c48518802 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -39,8 +39,14 @@ GIT websocket GIT + remote: https://github.com/rails/arel.git + revision: b26638ef041953992010590b31615c519fa0ea7d + specs: + arel (8.0.0) + +GIT remote: https://github.com/resque/resque.git - revision: 20d885065ac19e7f7d7a982f4ed1296083db0300 + revision: 835a4a7e61a2e33832dbf11975ecfab54af293c6 specs: resque (1.27.0) mono_logger (~> 1.0) @@ -49,19 +55,12 @@ GIT sinatra (>= 0.9.2) vegas (~> 0.1.2) -GIT - remote: https://github.com/sass/sass.git - revision: 7716e67f3507c6f65878c69aa49ec358ebf675c7 - branch: stable - specs: - sass (3.4.22) - PATH remote: . specs: actioncable (5.1.0.alpha) actionpack (= 5.1.0.alpha) - nio4r (~> 1.2) + nio4r (~> 2.0) websocket-driver (~> 0.6.1) actionmailer (5.1.0.alpha) actionpack (= 5.1.0.alpha) @@ -90,7 +89,7 @@ PATH activerecord (5.1.0.alpha) activemodel (= 5.1.0.alpha) activesupport (= 5.1.0.alpha) - arel (~> 7.0) + arel (~> 8.0) activesupport (5.1.0.alpha) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (~> 0.7) @@ -121,7 +120,6 @@ GEM addressable (2.5.0) public_suffix (~> 2.0, >= 2.0.2) amq-protocol (2.0.1) - arel (7.1.2) ast (2.3.0) backburner (1.3.1) beaneater (~> 1.0) @@ -132,7 +130,7 @@ GEM bcrypt (3.1.11-x86-mingw32) beaneater (1.0.0) benchmark-ips (2.7.2) - blade (0.6.1) + blade (0.7.0) activesupport (>= 3.0.0) blade-qunit_adapter (~> 2.0.1) coffee-script @@ -141,17 +139,16 @@ GEM eventmachine faye sprockets (>= 3.0) - sprockets-export (~> 0.9.1) thin (>= 1.6.0) thor (~> 0.19.1) useragent (~> 0.16.7) blade-qunit_adapter (2.0.1) - blade-sauce_labs_plugin (0.6.1) + blade-sauce_labs_plugin (0.6.2) childprocess faraday selenium-webdriver builder (3.2.2) - bunny (2.2.2) + bunny (2.6.2) amq-protocol (>= 2.0.1) byebug (9.0.6) childprocess (0.5.9) @@ -162,9 +159,9 @@ GEM coffee-script (2.4.1) coffee-script-source execjs - coffee-script-source (1.10.0) - concurrent-ruby (1.0.2) - connection_pool (2.2.0) + coffee-script-source (1.12.2) + concurrent-ruby (1.0.4) + connection_pool (2.2.1) cookiejar (0.3.3) curses (1.0.2) daemons (1.2.4) @@ -213,7 +210,7 @@ GEM railties (>= 4.2.0) thor (>= 0.14, < 2.0) json (2.0.2) - kindlerb (1.0.1) + kindlerb (1.2.0) mustache nokogiri libxml-ruby (2.9.0) @@ -241,22 +238,22 @@ GEM mysql2 (0.4.5) mysql2 (0.4.5-x64-mingw32) mysql2 (0.4.5-x86-mingw32) - nio4r (1.2.1) - nokogiri (1.6.8.1) + nio4r (2.0.0) + nokogiri (1.7.0) mini_portile2 (~> 2.1.0) - nokogiri (1.6.8.1-x64-mingw32) + nokogiri (1.7.0-x64-mingw32) mini_portile2 (~> 2.1.0) - nokogiri (1.6.8.1-x86-mingw32) + nokogiri (1.7.0-x86-mingw32) mini_portile2 (~> 2.1.0) - parser (2.3.2.0) + parser (2.3.3.1) ast (~> 2.2) pg (0.19.0) pg (0.19.0-x64-mingw32) pg (0.19.0-x86-mingw32) powerpack (0.1.1) - psych (2.1.1) + psych (2.2.2) public_suffix (2.0.4) - puma (3.6.0) + puma (3.6.2) qu (0.2.0) multi_json qu-redis (0.2.0) @@ -272,17 +269,17 @@ GEM rack rack-test (0.6.3) rack (>= 1.0) - rails-dom-testing (2.0.1) + rails-dom-testing (2.0.2) activesupport (>= 4.2.0, < 6.0) - nokogiri (~> 1.6.0) + nokogiri (~> 1.6) rails-html-sanitizer (1.0.3) loofah (~> 2.0) rainbow (2.1.0) - rake (11.3.0) - rb-fsevent (0.9.7) - rdoc (5.0.0.beta2) + rake (12.0.0) + rb-fsevent (0.9.8) + rdoc (5.0.0) redcarpet (3.2.3) - redis (3.3.1) + redis (3.3.2) redis-namespace (1.5.2) redis (~> 3.0, >= 3.0.4) resque-scheduler (4.3.0) @@ -290,56 +287,57 @@ GEM redis (~> 3.3) resque (~> 1.26) rufus-scheduler (~> 3.2) - rubocop (0.45.0) + rubocop (0.46.0) parser (>= 2.3.1.1, < 3.0) powerpack (~> 0.1) rainbow (>= 1.99.1, < 3.0) ruby-progressbar (~> 1.7) unicode-display_width (~> 1.0, >= 1.0.1) ruby-progressbar (1.8.1) - ruby_dep (1.4.0) + ruby_dep (1.5.0) rubyzip (1.2.0) - rufus-scheduler (3.2.2) + rufus-scheduler (3.3.1) + tzinfo + sass (3.4.23) sass-rails (5.0.6) railties (>= 4.0.0, < 6) sass (~> 3.1) sprockets (>= 2.8, < 4.0) sprockets-rails (>= 2.0, < 4.0) tilt (>= 1.1, < 3) - sdoc (1.0.0.beta2) - rdoc (= 5.0.0.beta2) - selenium-webdriver (3.0.1) + sdoc (1.0.0.rc1) + rdoc (= 5.0.0) + selenium-webdriver (3.0.5) childprocess (~> 0.5) rubyzip (~> 1.0) websocket (~> 1.0) - sequel (4.39.0) + sequel (4.41.0) serverengine (1.5.11) sigdump (~> 0.2.2) - sidekiq (4.2.2) + sidekiq (4.2.7) concurrent-ruby (~> 1.0) connection_pool (~> 2.2, >= 2.2.0) - rack-protection (~> 1.5) + rack-protection (>= 1.5.0) redis (~> 3.2, >= 3.2.1) sigdump (0.2.4) simple_uuid (0.4.0) sinatra (1.0) rack (>= 1.0) - sneakers (2.3.5) - bunny (~> 2.2.0) + sneakers (2.4.0) + bunny (~> 2.6) serverengine (~> 1.5.11) thor thread (~> 0.1.7) - sprockets (3.7.0) + sprockets (3.7.1) concurrent-ruby (~> 1.0) rack (> 1, < 3) - sprockets-export (0.9.1) sprockets-rails (3.2.0) actionpack (>= 4.0) activesupport (>= 4.0) sprockets (>= 3.0.0) - sqlite3 (1.3.12) - sqlite3 (1.3.12-x64-mingw32) - sqlite3 (1.3.12-x86-mingw32) + sqlite3 (1.3.13) + sqlite3 (1.3.13-x64-mingw32) + sqlite3 (1.3.13-x86-mingw32) stackprof (0.2.10) sucker_punch (2.0.2) concurrent-ruby (~> 1.0.0) @@ -347,7 +345,7 @@ GEM daemons (~> 1.0, >= 1.0.9) eventmachine (~> 1.0, >= 1.0.4) rack (>= 1, < 3) - thor (0.19.1) + thor (0.19.4) thread (0.1.7) thread_safe (0.3.5) tilt (2.0.5) @@ -356,17 +354,17 @@ GEM turbolinks-source (5.0.0) tzinfo (1.2.2) thread_safe (~> 0.1) - tzinfo-data (1.2016.7) + tzinfo-data (1.2016.10) tzinfo (>= 1.0.0) - uglifier (3.0.2) + uglifier (3.0.4) execjs (>= 0.3.0, < 3) - unicode-display_width (1.1.1) + unicode-display_width (1.1.2) useragent (0.16.8) vegas (0.1.11) rack (>= 1.0.0) - w3c_validators (1.2) - json - nokogiri + w3c_validators (1.3.1) + json (~> 2.0) + nokogiri (~> 1.6) wdm (0.1.1) websocket (1.2.3) websocket-driver (0.6.4) @@ -382,6 +380,7 @@ DEPENDENCIES activerecord-jdbcmysql-adapter (>= 1.3.0) activerecord-jdbcpostgresql-adapter (>= 1.3.0) activerecord-jdbcsqlite3-adapter (>= 1.3.0) + arel! backburner bcrypt (~> 3.1.11) benchmark-ips @@ -396,7 +395,7 @@ DEPENDENCIES hiredis jquery-rails json (>= 2.0.0) - kindlerb (>= 1.0.1) + kindlerb (~> 1.2.0) libxml-ruby listen (>= 3.0.5, < 3.2) minitest (< 5.3.4) @@ -419,9 +418,8 @@ DEPENDENCIES resque! resque-scheduler rubocop - sass! sass-rails - sdoc (= 1.0.0.beta2) + sdoc (= 1.0.0.rc1) sequel sidekiq sneakers @@ -436,4 +434,4 @@ DEPENDENCIES websocket-client-simple! BUNDLED WITH - 1.13.6 + 1.13.7 diff --git a/MIT-LICENSE b/MIT-LICENSE index 40235833ba..6b3cead1a7 100644 --- a/MIT-LICENSE +++ b/MIT-LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2005-2016 David Heinemeier Hansson +Copyright (c) 2005-2017 David Heinemeier Hansson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/actioncable/CHANGELOG.md b/actioncable/CHANGELOG.md index 2c84d3158f..4f17274a01 100644 --- a/actioncable/CHANGELOG.md +++ b/actioncable/CHANGELOG.md @@ -13,12 +13,12 @@ *Vladimir Dementyev* -* Buffer now writes to websocket connections, to avoid blocking threads +* Buffer now writes to WebSocket connections, to avoid blocking threads that could be doing more useful things. *Matthew Draper*, *Tinco Andringa* -* Protect against concurrent writes to a websocket connection from +* Protect against concurrent writes to a WebSocket connection from multiple threads; the underlying OS write is not always threadsafe. *Tinco Andringa* diff --git a/actioncable/MIT-LICENSE b/actioncable/MIT-LICENSE index 27a17cf41b..1a0e653b69 100644 --- a/actioncable/MIT-LICENSE +++ b/actioncable/MIT-LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2015-2016 Basecamp, LLC +Copyright (c) 2015-2017 Basecamp, LLC Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/actioncable/README.md b/actioncable/README.md index 8ad9aeb1f1..c55b7dc57b 100644 --- a/actioncable/README.md +++ b/actioncable/README.md @@ -51,7 +51,7 @@ module ApplicationCable self.current_user = find_verified_user end - protected + private def find_verified_user if current_user = User.find_by(id: cookies.signed[:user_id]) current_user @@ -536,6 +536,15 @@ cable.subscriptions.create 'AppearanceChannel', # normal channel code goes here... ``` +## Download and Installation + +The latest version of Action Cable can be installed with [RubyGems](#gem-usage), +or with [npm](#npm-usage). + +Source code can be downloaded as part of the Rails project on GitHub + +* https://github.com/rails/rails/tree/master/actioncable + ## License Action Cable is released under the MIT license: diff --git a/actioncable/actioncable.gemspec b/actioncable/actioncable.gemspec index e7f91d0e34..6d95f022fa 100644 --- a/actioncable/actioncable.gemspec +++ b/actioncable/actioncable.gemspec @@ -20,6 +20,6 @@ Gem::Specification.new do |s| s.add_dependency "actionpack", version - s.add_dependency "nio4r", "~> 1.2" + s.add_dependency "nio4r", "~> 2.0" s.add_dependency "websocket-driver", "~> 0.6.1" end diff --git a/actioncable/app/assets/javascripts/action_cable/connection.coffee b/actioncable/app/assets/javascripts/action_cable/connection.coffee index 29ad676290..7fd68cad2f 100644 --- a/actioncable/app/assets/javascripts/action_cable/connection.coffee +++ b/actioncable/app/assets/javascripts/action_cable/connection.coffee @@ -23,7 +23,7 @@ class ActionCable.Connection open: => if @isActive() ActionCable.log("Attempted to open WebSocket, but existing socket is #{@getState()}") - throw new Error("Existing connection must be closed before opening") + false else ActionCable.log("Opening WebSocket, current state is #{@getState()}, subprotocols: #{protocols}") @uninstallEventHandlers() if @webSocket? diff --git a/actioncable/lib/action_cable.rb b/actioncable/lib/action_cable.rb index d353716636..c2d3550acb 100644 --- a/actioncable/lib/action_cable.rb +++ b/actioncable/lib/action_cable.rb @@ -1,5 +1,5 @@ #-- -# Copyright (c) 2015-2016 Basecamp, LLC +# Copyright (c) 2015-2017 Basecamp, LLC # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the diff --git a/actioncable/lib/action_cable/channel/base.rb b/actioncable/lib/action_cable/channel/base.rb index a866044f95..6739a62ba0 100644 --- a/actioncable/lib/action_cable/channel/base.rb +++ b/actioncable/lib/action_cable/channel/base.rb @@ -122,16 +122,16 @@ module ActionCable end end - protected + private # action_methods are cached and there is sometimes need to refresh # them. ::clear_action_methods! allows you to do that, so next time # you run action_methods, they will be recalculated. - def clear_action_methods! + def clear_action_methods! # :doc: @action_methods = nil end # Refresh the cached action_methods when a new action_method is added. - def method_added(name) + def method_added(name) # :doc: super clear_action_methods! end @@ -189,22 +189,22 @@ module ActionCable end end - protected + private # Called once a consumer has become a subscriber of the channel. Usually the place to setup any streams # you want this channel to be sending to the subscriber. - def subscribed + def subscribed # :doc: # Override in subclasses end # Called once a consumer has cut its cable connection. Can be used for cleaning up connections or marking # users as offline or the like. - def unsubscribed + def unsubscribed # :doc: # Override in subclasses end # Transmit a hash of data to the subscriber. The hash will automatically be wrapped in a JSON envelope with # the proper channel identifier marked as the recipient. - def transmit(data, via: nil) + def transmit(data, via: nil) # :doc: logger.info "#{self.class.name} transmitting #{data.inspect.truncate(300)}".tap { |m| m << " (via #{via})" if via } payload = { channel_class: self.class.name, data: data, via: via } @@ -213,33 +213,32 @@ module ActionCable end end - def ensure_confirmation_sent + def ensure_confirmation_sent # :doc: return if subscription_rejected? @defer_subscription_confirmation_counter.decrement transmit_subscription_confirmation unless defer_subscription_confirmation? end - def defer_subscription_confirmation! + def defer_subscription_confirmation! # :doc: @defer_subscription_confirmation_counter.increment end - def defer_subscription_confirmation? + def defer_subscription_confirmation? # :doc: @defer_subscription_confirmation_counter.value > 0 end - def subscription_confirmation_sent? + def subscription_confirmation_sent? # :doc: @subscription_confirmation_sent end - def reject + def reject # :doc: @reject_subscription = true end - def subscription_rejected? + def subscription_rejected? # :doc: @reject_subscription end - private def delegate_connection_identifiers connection.identifiers.each do |identifier| define_singleton_method(identifier) do diff --git a/actioncable/lib/action_cable/connection/base.rb b/actioncable/lib/action_cable/connection/base.rb index e1da126d64..0a517a532d 100644 --- a/actioncable/lib/action_cable/connection/base.rb +++ b/actioncable/lib/action_cable/connection/base.rb @@ -22,7 +22,7 @@ module ActionCable # # Any cleanup work needed when the cable connection is cut. # end # - # protected + # private # def find_verified_user # User.find_by_identity(cookies.signed[:identity_id]) || # reject_unauthorized_connection @@ -133,9 +133,15 @@ module ActionCable send_async :handle_close end + # TODO Change this to private once we've dropped Ruby 2.2 support. + # Workaround for Ruby 2.2 "private attribute?" warning. protected + attr_reader :websocket + attr_reader :message_buffer + + private # The request that initiated the WebSocket connection is available here. This gives access to the environment, cookies, etc. - def request + def request # :doc: @request ||= begin environment = Rails.application.env_config.merge(env) if defined?(Rails.application) && Rails.application ActionDispatch::Request.new(environment || env) @@ -143,14 +149,10 @@ module ActionCable end # The cookies of the request that initiated the WebSocket connection. Useful for performing authorization checks. - def cookies + def cookies # :doc: request.cookie_jar end - attr_reader :websocket - attr_reader :message_buffer - - private def encode(cable_message) @coder.encode cable_message end diff --git a/actioncable/lib/action_cable/connection/message_buffer.rb b/actioncable/lib/action_cable/connection/message_buffer.rb index 6a80770cae..4ccd322644 100644 --- a/actioncable/lib/action_cable/connection/message_buffer.rb +++ b/actioncable/lib/action_cable/connection/message_buffer.rb @@ -28,6 +28,8 @@ module ActionCable receive_buffered_messages end + # TODO Change this to private once we've dropped Ruby 2.2 support. + # Workaround for Ruby 2.2 "private attribute?" warning. protected attr_reader :connection attr_reader :buffered_messages diff --git a/actioncable/lib/action_cable/connection/subscriptions.rb b/actioncable/lib/action_cable/connection/subscriptions.rb index 00511aead5..abf42c99d5 100644 --- a/actioncable/lib/action_cable/connection/subscriptions.rb +++ b/actioncable/lib/action_cable/connection/subscriptions.rb @@ -61,6 +61,8 @@ module ActionCable subscriptions.each { |id, channel| remove_subscription(channel) } end + # TODO Change this to private once we've dropped Ruby 2.2 support. + # Workaround for Ruby 2.2 "private attribute?" warning. protected attr_reader :connection, :subscriptions diff --git a/actioncable/lib/action_cable/connection/tagged_logger_proxy.rb b/actioncable/lib/action_cable/connection/tagged_logger_proxy.rb index 41afa9680a..aef549aa86 100644 --- a/actioncable/lib/action_cable/connection/tagged_logger_proxy.rb +++ b/actioncable/lib/action_cable/connection/tagged_logger_proxy.rb @@ -31,8 +31,8 @@ module ActionCable end end - protected - def log(type, message) + private + def log(type, message) # :doc: tag(@logger) { @logger.send type, message } end end diff --git a/actioncable/lib/action_cable/connection/web_socket.rb b/actioncable/lib/action_cable/connection/web_socket.rb index 382141b89f..03eb6e2ea8 100644 --- a/actioncable/lib/action_cable/connection/web_socket.rb +++ b/actioncable/lib/action_cable/connection/web_socket.rb @@ -32,6 +32,8 @@ module ActionCable websocket.rack_response end + # TODO Change this to private once we've dropped Ruby 2.2 support. + # Workaround for Ruby 2.2 "private attribute?" warning. protected attr_reader :websocket end diff --git a/actioncable/lib/action_cable/engine.rb b/actioncable/lib/action_cable/engine.rb index e23527b84e..63a26636a0 100644 --- a/actioncable/lib/action_cable/engine.rb +++ b/actioncable/lib/action_cable/engine.rb @@ -31,7 +31,7 @@ module ActionCable self.cable = Rails.application.config_for(config_path).with_indifferent_access end - previous_connection_class = self.connection_class + previous_connection_class = connection_class self.connection_class = -> { "ApplicationCable::Connection".safe_constantize || previous_connection_class.call } options.each { |k, v| send("#{k}=", v) } diff --git a/actioncable/lib/rails/generators/channel/USAGE b/actioncable/lib/rails/generators/channel/USAGE index 6249553c22..dd109fda80 100644 --- a/actioncable/lib/rails/generators/channel/USAGE +++ b/actioncable/lib/rails/generators/channel/USAGE @@ -3,7 +3,7 @@ Description: Stubs out a new cable channel for the server (in Ruby) and client (in CoffeeScript). Pass the channel name, either CamelCased or under_scored, and an optional list of channel actions as arguments. - Note: Turn on the cable connection in app/assets/javascript/cable.js after generating any channels. + Note: Turn on the cable connection in app/assets/javascripts/cable.js after generating any channels. Example: ======== @@ -11,4 +11,4 @@ Example: creates a Chat channel class and CoffeeScript asset: Channel: app/channels/chat_channel.rb - Assets: app/assets/javascript/channels/chat.coffee + Assets: app/assets/javascripts/channels/chat.coffee diff --git a/actioncable/lib/rails/generators/channel/channel_generator.rb b/actioncable/lib/rails/generators/channel/channel_generator.rb index 20d807c033..984b78bc9c 100644 --- a/actioncable/lib/rails/generators/channel/channel_generator.rb +++ b/actioncable/lib/rails/generators/channel/channel_generator.rb @@ -13,7 +13,7 @@ module Rails template "channel.rb", File.join("app/channels", class_path, "#{file_name}_channel.rb") if options[:assets] - if self.behavior == :invoke + if behavior == :invoke template "assets/cable.js", "app/assets/javascripts/cable.js" end @@ -23,14 +23,14 @@ module Rails generate_application_cable_files end - protected + private def file_name @_file_name ||= super.gsub(/_channel/i, "") end # FIXME: Change these files to symlinks once RubyGems 2.5.0 is required. def generate_application_cable_files - return if self.behavior != :invoke + return if behavior != :invoke files = [ "application_cable/channel.rb", diff --git a/actioncable/test/connection/identifier_test.rb b/actioncable/test/connection/identifier_test.rb index a4dfcc06f0..f3d3bc0603 100644 --- a/actioncable/test/connection/identifier_test.rb +++ b/actioncable/test/connection/identifier_test.rb @@ -53,7 +53,7 @@ class ActionCable::Connection::IdentifierTest < ActionCable::TestCase end end - protected + private def open_connection_with_stubbed_pubsub server = TestServer.new server.stubs(:adapter).returns(stub_everything("adapter")) diff --git a/actioncable/test/connection/multiple_identifiers_test.rb b/actioncable/test/connection/multiple_identifiers_test.rb index 67e68355c5..ca1a08f4d6 100644 --- a/actioncable/test/connection/multiple_identifiers_test.rb +++ b/actioncable/test/connection/multiple_identifiers_test.rb @@ -19,7 +19,7 @@ class ActionCable::Connection::MultipleIdentifiersTest < ActionCable::TestCase end end - protected + private def open_connection_with_stubbed_pubsub server = TestServer.new server.stubs(:pubsub).returns(stub_everything("pubsub")) diff --git a/actioncable/test/connection/string_identifier_test.rb b/actioncable/test/connection/string_identifier_test.rb index 87484765e5..6d53e249cb 100644 --- a/actioncable/test/connection/string_identifier_test.rb +++ b/actioncable/test/connection/string_identifier_test.rb @@ -21,7 +21,7 @@ class ActionCable::Connection::StringIdentifierTest < ActionCable::TestCase end end - protected + private def open_connection_with_stubbed_pubsub @server = TestServer.new @server.stubs(:pubsub).returns(stub_everything("pubsub")) diff --git a/actioncable/test/javascript/src/unit/consumer_test.coffee b/actioncable/test/javascript/src/unit/consumer_test.coffee index cf8a592255..41445274eb 100644 --- a/actioncable/test/javascript/src/unit/consumer_test.coffee +++ b/actioncable/test/javascript/src/unit/consumer_test.coffee @@ -2,8 +2,11 @@ {consumerTest} = ActionCable.TestHelpers module "ActionCable.Consumer", -> - consumerTest "#connect", connect: false, ({consumer, server, done}) -> - server.on("connection", done) + consumerTest "#connect", connect: false, ({consumer, server, assert, done}) -> + server.on "connection", -> + assert.equal consumer.connect(), false + done() + consumer.connect() consumerTest "#disconnect", ({consumer, client, done}) -> diff --git a/actionmailer/CHANGELOG.md b/actionmailer/CHANGELOG.md index 3b9f503a0b..de8abcccfe 100644 --- a/actionmailer/CHANGELOG.md +++ b/actionmailer/CHANGELOG.md @@ -1,3 +1,15 @@ +* Mime type: allow to custom content type when setting body in headers + and attachments. + + Example: + + def test_emails + attachments["invoice.pdf"] = "This is test File content" + mail(body: "Hello there", content_type: "text/html") + end + + *Minh Quy* + * Exception handling: use `rescue_from` to handle exceptions raised by mailer actions, by message delivery, and by deferred delivery jobs. diff --git a/actionmailer/MIT-LICENSE b/actionmailer/MIT-LICENSE index 8573eb1225..ac810e86d0 100644 --- a/actionmailer/MIT-LICENSE +++ b/actionmailer/MIT-LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2016 David Heinemeier Hansson +Copyright (c) 2004-2017 David Heinemeier Hansson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/actionmailer/lib/action_mailer.rb b/actionmailer/lib/action_mailer.rb index 668bc99435..cf2c0f37e3 100644 --- a/actionmailer/lib/action_mailer.rb +++ b/actionmailer/lib/action_mailer.rb @@ -1,5 +1,5 @@ #-- -# Copyright (c) 2004-2016 David Heinemeier Hansson +# Copyright (c) 2004-2017 David Heinemeier Hansson # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 1f5738bbab..8205743de3 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -208,6 +208,19 @@ module ActionMailer # end # end # + # You can also send attachments with html template, in this case you need to add body, attachments, + # and custom content type like this: + # + # class NotifierMailer < ApplicationMailer + # def welcome(recipient) + # attachments["free_book.pdf"] = File.read("path/to/file.pdf") + # mail(to: recipient, + # subject: "New account information", + # content_type: "text/html", + # body: "<html><body>Hello there</body></html>") + # end + # end + # # = Inline Attachments # # You can also specify that a file should be displayed inline with other HTML. This is useful @@ -544,9 +557,9 @@ module ActionMailer end end - protected + private - def set_payload_for_mail(payload, mail) #:nodoc: + def set_payload_for_mail(payload, mail) payload[:mailer] = name payload[:message_id] = mail.message_id payload[:subject] = mail.subject @@ -558,7 +571,7 @@ module ActionMailer payload[:mail] = mail.encoded end - def method_missing(method_name, *args) # :nodoc: + def method_missing(method_name, *args) if action_methods.include?(method_name.to_s) MessageDelivery.new(self, method_name, *args) else @@ -566,9 +579,7 @@ module ActionMailer end end - private - - def respond_to_missing?(method, include_all = false) #:nodoc: + def respond_to_missing?(method, include_all = false) action_methods.include?(method.to_s) end end @@ -830,7 +841,7 @@ module ActionMailer message end - protected + private # Used by #mail to set the content type of the message. # @@ -841,7 +852,7 @@ module ActionMailer # If there is no content type passed in via headers, and there are no # attachments, or the message is multipart, then the default content type is # used. - def set_content_type(m, user_content_type, class_default) + def set_content_type(m, user_content_type, class_default) # :doc: params = m.content_type_parameters || {} case when user_content_type.present? @@ -863,18 +874,16 @@ module ActionMailer # If it does not find a translation for the +subject+ under the specified scope it will default to a # humanized version of the <tt>action_name</tt>. # If the subject has interpolations, you can pass them through the +interpolations+ parameter. - def default_i18n_subject(interpolations = {}) + def default_i18n_subject(interpolations = {}) # :doc: mailer_scope = self.class.mailer_name.tr("/", ".") I18n.t(:subject, interpolations.merge(scope: [mailer_scope, action_name], default: action_name.humanize)) end # Emails do not support relative path links. - def self.supports_path? + def self.supports_path? # :doc: false end - private - def apply_defaults(headers) default_values = self.class.default.map do |key, value| [ @@ -900,15 +909,19 @@ module ActionMailer yield(collector) collector.responses elsif headers[:body] - [{ - body: headers.delete(:body), - content_type: self.class.default[:content_type] || "text/plain" - }] + collect_responses_from_text(headers) else collect_responses_from_templates(headers) end end + def collect_responses_from_text(headers) + [{ + body: headers.delete(:body), + content_type: headers[:content_type] || "text/plain" + }] + end + def collect_responses_from_templates(headers) templates_path = headers[:template_path] || self.class.mailer_name templates_name = headers[:template_name] || action_name diff --git a/actionmailer/lib/action_mailer/delivery_methods.rb b/actionmailer/lib/action_mailer/delivery_methods.rb index be98f4c65e..bcc4ef03cf 100644 --- a/actionmailer/lib/action_mailer/delivery_methods.rb +++ b/actionmailer/lib/action_mailer/delivery_methods.rb @@ -59,7 +59,7 @@ module ActionMailer end def wrap_delivery_behavior(mail, method = nil, options = nil) # :nodoc: - method ||= self.delivery_method + method ||= delivery_method mail.delivery_handler = self case method diff --git a/actionmailer/lib/action_mailer/preview.rb b/actionmailer/lib/action_mailer/preview.rb index 93a11453bf..b0152aff03 100644 --- a/actionmailer/lib/action_mailer/preview.rb +++ b/actionmailer/lib/action_mailer/preview.rb @@ -63,7 +63,7 @@ module ActionMailer # interceptors will be informed so that they can transform the message # as they would if the mail was actually being delivered. def call(email) - preview = self.new + preview = new message = preview.public_send(email) inform_preview_interceptors(message) message @@ -94,22 +94,22 @@ module ActionMailer name.sub(/Preview$/, "").underscore end - protected - def load_previews #:nodoc: + private + def load_previews if preview_path Dir["#{preview_path}/**/*_preview.rb"].each { |file| require_dependency file } end end - def preview_path #:nodoc: + def preview_path Base.preview_path end - def show_previews #:nodoc: + def show_previews Base.show_previews end - def inform_preview_interceptors(message) #:nodoc: + def inform_preview_interceptors(message) Base.preview_interceptors.each do |interceptor| interceptor.previewing_email(message) end diff --git a/actionmailer/lib/action_mailer/test_case.rb b/actionmailer/lib/action_mailer/test_case.rb index 1d09a4ee96..cbd841466d 100644 --- a/actionmailer/lib/action_mailer/test_case.rb +++ b/actionmailer/lib/action_mailer/test_case.rb @@ -57,7 +57,7 @@ module ActionMailer end def mailer_class - if mailer = self._mailer_class + if mailer = _mailer_class mailer else tests determine_default_mailer(name) @@ -73,38 +73,36 @@ module ActionMailer end end - protected + private - def initialize_test_deliveries # :nodoc: + def initialize_test_deliveries set_delivery_method :test @old_perform_deliveries = ActionMailer::Base.perform_deliveries ActionMailer::Base.perform_deliveries = true ActionMailer::Base.deliveries.clear end - def restore_test_deliveries # :nodoc: + def restore_test_deliveries restore_delivery_method ActionMailer::Base.perform_deliveries = @old_perform_deliveries end - def set_delivery_method(method) # :nodoc: + def set_delivery_method(method) @old_delivery_method = ActionMailer::Base.delivery_method ActionMailer::Base.delivery_method = method end - def restore_delivery_method # :nodoc: + def restore_delivery_method ActionMailer::Base.deliveries.clear ActionMailer::Base.delivery_method = @old_delivery_method end - def set_expected_mail # :nodoc: + def set_expected_mail @expected = Mail.new @expected.content_type ["text", "plain", { "charset" => charset }] @expected.mime_version = "1.0" end - private - def charset "UTF-8" end diff --git a/actionmailer/lib/rails/generators/mailer/mailer_generator.rb b/actionmailer/lib/rails/generators/mailer/mailer_generator.rb index 9dd7ee7a27..99fe4544f1 100644 --- a/actionmailer/lib/rails/generators/mailer/mailer_generator.rb +++ b/actionmailer/lib/rails/generators/mailer/mailer_generator.rb @@ -11,7 +11,7 @@ module Rails template "mailer.rb", File.join("app/mailers", class_path, "#{file_name}_mailer.rb") in_root do - if self.behavior == :invoke && !File.exist?(application_mailer_file_name) + if behavior == :invoke && !File.exist?(application_mailer_file_name) template "application_mailer.rb", application_mailer_file_name end end @@ -19,12 +19,11 @@ module Rails hook_for :template_engine, :test_framework - protected - def file_name + private + def file_name # :doc: @_file_name ||= super.gsub(/_mailer/i, "") end - private def application_mailer_file_name @_application_mailer_file_name ||= if mountable_engine? "app/mailers/#{namespaced_path}/application_mailer.rb" diff --git a/actionmailer/test/abstract_unit.rb b/actionmailer/test/abstract_unit.rb index 82f03f5cba..51de03c4ce 100644 --- a/actionmailer/test/abstract_unit.rb +++ b/actionmailer/test/abstract_unit.rb @@ -3,8 +3,8 @@ require "active_support/core_ext/kernel/reporting" # These are the normal settings that will be set up by Railties # TODO: Have these tests support other combinations of these values silence_warnings do - Encoding.default_internal = "UTF-8" - Encoding.default_external = "UTF-8" + Encoding.default_internal = Encoding::UTF_8 + Encoding.default_external = Encoding::UTF_8 end module Rails diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb index 3bca69890d..490aaf33fc 100644 --- a/actionmailer/test/base_test.rb +++ b/actionmailer/test/base_test.rb @@ -140,6 +140,11 @@ class BaseTest < ActiveSupport::TestCase assert_equal("multipart/mixed", email.mime_type) end + test "set mime type to text/html when attachment is included and body is set" do + email = BaseMailer.attachment_with_content(body: "Hello there", content_type: "text/html") + assert_equal("text/html", email.mime_type) + end + test "adds the rendered template as part" do email = BaseMailer.attachment_with_content assert_equal(2, email.parts.length) @@ -829,7 +834,7 @@ class BaseTest < ActiveSupport::TestCase assert_equal "special indeed!", mail["X-Special-Header"].to_s end - protected + private # Execute the block setting the given values and restoring old values after # the block is executed. diff --git a/actionmailer/test/i18n_with_controller_test.rb b/actionmailer/test/i18n_with_controller_test.rb index 039685ffe5..4f09339800 100644 --- a/actionmailer/test/i18n_with_controller_test.rb +++ b/actionmailer/test/i18n_with_controller_test.rb @@ -57,16 +57,14 @@ class ActionMailerI18nWithControllerTest < ActionDispatch::IntegrationTest stub_any_instance(Mail::SMTP, instance: Mail::SMTP.new({})) do |instance| assert_called(instance, :deliver!) do with_translation "de", email_subject: "[Anmeldung] Willkommen" do - ActiveSupport::Deprecation.silence do - get "/test/send_mail" - end + get "/test/send_mail" assert_equal "Mail sent - Subject: [Anmeldung] Willkommen", @response.body end end end end - protected + private def with_translation(locale, data) I18n.backend.store_translations(locale, data) diff --git a/actionmailer/test/mail_helper_test.rb b/actionmailer/test/mail_helper_test.rb index 972629e477..a2e04b5f48 100644 --- a/actionmailer/test/mail_helper_test.rb +++ b/actionmailer/test/mail_helper_test.rb @@ -65,7 +65,7 @@ The second end end - protected + private def mail_with_defaults(&block) mail(to: "test@localhost", from: "tester@example.com", diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 3123fc9786..425a9d9e87 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,15 +1,27 @@ +* Remove deprecated `ActionController::Metal.call`. + + *Rafael Mendonça França* + +* Remove deprecated `ActionController::Metal#env`. + + *Rafael Mendonça França* + +* Make `with_routing` test helper work when testing controllers inheriting from `ActionController::API` + + *Julia López* + * Use accept header in integration tests with `as: :json` - Instead of appending the `format` to the request path. Rails will figure + Instead of appending the `format` to the request path, Rails will figure out the format from the header instead. This allows devs to use `:as` on routes that don't have a format. - + Fixes #27144. *Kasper Timm Hansen* -* Reset a new session directly after its creation in ActionDispatch::IntegrationTest#open_session. +* Reset a new session directly after its creation in `ActionDispatch::IntegrationTest#open_session`. Fixes #22742. diff --git a/actionpack/MIT-LICENSE b/actionpack/MIT-LICENSE index 8573eb1225..ac810e86d0 100644 --- a/actionpack/MIT-LICENSE +++ b/actionpack/MIT-LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2016 David Heinemeier Hansson +Copyright (c) 2004-2017 David Heinemeier Hansson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/actionpack/lib/abstract_controller/caching.rb b/actionpack/lib/abstract_controller/caching.rb index d222880922..26e3f08bc1 100644 --- a/actionpack/lib/abstract_controller/caching.rb +++ b/actionpack/lib/abstract_controller/caching.rb @@ -52,9 +52,9 @@ module AbstractController self.class._view_cache_dependencies.map { |dep| instance_exec(&dep) }.compact end - protected + private # Convenience accessor. - def cache(key, options = {}, &block) + def cache(key, options = {}, &block) # :doc: if cache_configured? cache_store.fetch(ActiveSupport::Cache.expand_cache_key(key, :controller), options, &block) else diff --git a/actionpack/lib/abstract_controller/collector.rb b/actionpack/lib/abstract_controller/collector.rb index 57714b0588..40ae5aa1ca 100644 --- a/actionpack/lib/abstract_controller/collector.rb +++ b/actionpack/lib/abstract_controller/collector.rb @@ -19,7 +19,7 @@ module AbstractController generate_method_for_mime(mime) unless instance_methods.include?(mime.to_sym) end - protected + private def method_missing(symbol, &block) unless mime_constant = Mime[symbol] diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb index d339580435..54af938a93 100644 --- a/actionpack/lib/abstract_controller/rendering.rb +++ b/actionpack/lib/abstract_controller/rendering.rb @@ -109,6 +109,9 @@ module AbstractController def _process_format(format) end + def _process_variant(options) + end + def _set_html_content_type # :nodoc: end @@ -119,10 +122,7 @@ module AbstractController # :api: private def _normalize_render(*args, &block) options = _normalize_args(*args, &block) - #TODO: remove defined? when we restore AP <=> AV dependency - if defined?(request) && !request.nil? && request.variant.present? - options[:variant] = request.variant - end + _process_variant(options) _normalize_options(options) options end diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb index ed93a2f09c..337718afc0 100644 --- a/actionpack/lib/action_controller/metal.rb +++ b/actionpack/lib/action_controller/metal.rb @@ -118,11 +118,6 @@ module ActionController class Metal < AbstractController::Base abstract! - def env - @_request.env - end - deprecate :env - # Returns the last part of the controller's name, underscored, without the ending # <tt>Controller</tt>. For instance, PostsController returns <tt>posts</tt>. # Namespaces are left out, so Admin::PostsController returns <tt>posts</tt> as well. @@ -139,8 +134,8 @@ module ActionController end end - def self.encoding_for_param(action, param) # :nodoc: - ::Encoding::UTF_8 + def self.binary_params_for?(action) # :nodoc: + false end # Delegates to the class' <tt>controller_name</tt> @@ -232,14 +227,6 @@ module ActionController middleware_stack end - # Makes the controller a Rack endpoint that runs the action in the given - # +env+'s +action_dispatch.request.path_parameters+ key. - def self.call(env) - req = ActionDispatch::Request.new env - action(req.path_parameters[:action]).call(env) - end - class << self; deprecate :call; end - # Returns a Rack endpoint for the given action name. def self.action(name) if middleware_stack.any? diff --git a/actionpack/lib/action_controller/metal/data_streaming.rb b/actionpack/lib/action_controller/metal/data_streaming.rb index f089c8423b..731e03e2fc 100644 --- a/actionpack/lib/action_controller/metal/data_streaming.rb +++ b/actionpack/lib/action_controller/metal/data_streaming.rb @@ -11,7 +11,7 @@ module ActionController #:nodoc: DEFAULT_SEND_FILE_TYPE = "application/octet-stream".freeze #:nodoc: DEFAULT_SEND_FILE_DISPOSITION = "attachment".freeze #:nodoc: - protected + private # Sends the file. This uses a server-appropriate method (such as X-Sendfile) # via the Rack::Sendfile middleware. The header to use is set via # +config.action_dispatch.x_sendfile_header+. @@ -70,7 +70,6 @@ module ActionController #:nodoc: send_file_headers! options self.status = options[:status] || 200 - self.content_type = options[:type] if options.key?(:type) self.content_type = options[:content_type] if options.key?(:content_type) response.send_file path end @@ -109,10 +108,12 @@ module ActionController #:nodoc: render options.slice(:status, :content_type).merge(body: data) end - private def send_file_headers!(options) type_provided = options.has_key?(:type) + self.content_type = DEFAULT_SEND_FILE_TYPE + response.sending_file = true + content_type = options.fetch(:type, DEFAULT_SEND_FILE_TYPE) raise ArgumentError, ":type option required" if content_type.nil? @@ -137,8 +138,6 @@ module ActionController #:nodoc: headers["Content-Transfer-Encoding"] = "binary" - response.sending_file = true - # Fix a problem with IE 6.0 on opening downloaded files: # If Cache-Control: no-cache is set (which Rails does by default), # IE removes the file it just downloaded from its cache immediately diff --git a/actionpack/lib/action_controller/metal/flash.rb b/actionpack/lib/action_controller/metal/flash.rb index 65351284b9..347fbf0e74 100644 --- a/actionpack/lib/action_controller/metal/flash.rb +++ b/actionpack/lib/action_controller/metal/flash.rb @@ -42,7 +42,7 @@ module ActionController #:nodoc: end end - protected + private def redirect_to(options = {}, response_status_and_flash = {}) #:doc: self.class._flash_types.each do |flash_type| if type = response_status_and_flash.delete(flash_type) diff --git a/actionpack/lib/action_controller/metal/head.rb b/actionpack/lib/action_controller/metal/head.rb index 4dff23dd85..0c50894bce 100644 --- a/actionpack/lib/action_controller/metal/head.rb +++ b/actionpack/lib/action_controller/metal/head.rb @@ -37,7 +37,7 @@ module ActionController if include_content?(response_code) self.content_type = content_type || (Mime[formats.first] if formats) - self.response.charset = false + response.charset = false end true diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb index 5bf0a99fe4..0575360068 100644 --- a/actionpack/lib/action_controller/metal/http_authentication.rb +++ b/actionpack/lib/action_controller/metal/http_authentication.rb @@ -28,7 +28,7 @@ module ActionController # class ApplicationController < ActionController::Base # before_action :set_account, :authenticate # - # protected + # private # def set_account # @account = Account.find_by(url_name: request.subdomains.first) # end @@ -363,7 +363,7 @@ module ActionController # class ApplicationController < ActionController::Base # before_action :set_account, :authenticate # - # protected + # private # def set_account # @account = Account.find_by(url_name: request.subdomains.first) # end diff --git a/actionpack/lib/action_controller/metal/instrumentation.rb b/actionpack/lib/action_controller/metal/instrumentation.rb index f83396ae55..924686218f 100644 --- a/actionpack/lib/action_controller/metal/instrumentation.rb +++ b/actionpack/lib/action_controller/metal/instrumentation.rb @@ -83,14 +83,14 @@ module ActionController # end # # :api: plugin - def cleanup_view_runtime #:nodoc: + def cleanup_view_runtime yield end # Every time after an action is processed, this method is invoked # with the payload, so you can add more information. # :api: plugin - def append_info_to_payload(payload) #:nodoc: + def append_info_to_payload(payload) payload[:view_runtime] = view_runtime end diff --git a/actionpack/lib/action_controller/metal/parameter_encoding.rb b/actionpack/lib/action_controller/metal/parameter_encoding.rb index c457fd0d06..962532ff09 100644 --- a/actionpack/lib/action_controller/metal/parameter_encoding.rb +++ b/actionpack/lib/action_controller/metal/parameter_encoding.rb @@ -1,5 +1,5 @@ module ActionController - # Allows encoding to be specified per parameter per action. + # Specify binary encoding for parameters for a given action. module ParameterEncoding extend ActiveSupport::Concern @@ -13,17 +13,36 @@ module ActionController @_parameter_encodings = {} end - def encoding_for_param(action, param) # :nodoc: - if @_parameter_encodings[action.to_s] && @_parameter_encodings[action.to_s][param.to_s] - @_parameter_encodings[action.to_s][param.to_s] - else - super - end + def binary_params_for?(action) # :nodoc: + @_parameter_encodings[action.to_s] end - def parameter_encoding(action, param_name, encoding) - @_parameter_encodings[action.to_s] ||= {} - @_parameter_encodings[action.to_s][param_name.to_s] = encoding + # Specify that a given action's parameters should all be encoded as + # ASCII-8BIT (it "skips" the encoding default of UTF-8). + # + # For example, a controller would use it like this: + # + # class RepositoryController < ActionController::Base + # skip_parameter_encoding :show + # + # def show + # @repo = Repository.find_by_filesystem_path params[:file_path] + # + # # `repo_name` is guaranteed to be UTF-8, but was ASCII-8BIT, so + # # tag it as such + # @repo_name = params[:repo_name].force_encoding 'UTF-8' + # end + # + # def index + # @repositories = Repository.all + # end + # end + # + # The show action in the above controller would have all parameter values + # encoded as ASCII-8BIT. This is useful in the case where an application + # must handle data but encoding of the data is unknown, like file system data. + def skip_parameter_encoding(action) + @_parameter_encodings[action.to_s] = true end end end diff --git a/actionpack/lib/action_controller/metal/params_wrapper.rb b/actionpack/lib/action_controller/metal/params_wrapper.rb index 86e817fe16..7fc898f034 100644 --- a/actionpack/lib/action_controller/metal/params_wrapper.rb +++ b/actionpack/lib/action_controller/metal/params_wrapper.rb @@ -135,7 +135,7 @@ module ActionController # # This method also does namespace lookup. Foo::Bar::UsersController will # try to find Foo::Bar::User, Foo::User and finally User. - def _default_wrap_model #:nodoc: + def _default_wrap_model return nil if klass.anonymous? model_name = klass.name.sub(/Controller$/, "").classify diff --git a/actionpack/lib/action_controller/metal/redirecting.rb b/actionpack/lib/action_controller/metal/redirecting.rb index 30798c1d99..4dfcf4da28 100644 --- a/actionpack/lib/action_controller/metal/redirecting.rb +++ b/actionpack/lib/action_controller/metal/redirecting.rb @@ -50,7 +50,7 @@ module ActionController # redirect_to post_url(@post), status: 301, flash: { updated_post_id: @post.id } # redirect_to({ action: 'atom' }, alert: "Something serious happened") # - def redirect_to(options = {}, response_status = {}) #:doc: + def redirect_to(options = {}, response_status = {}) raise ActionControllerError.new("Cannot redirect to nil!") unless options raise AbstractController::DoubleRenderError if response_body diff --git a/actionpack/lib/action_controller/metal/renderers.rb b/actionpack/lib/action_controller/metal/renderers.rb index f8a037189c..733aca195d 100644 --- a/actionpack/lib/action_controller/metal/renderers.rb +++ b/actionpack/lib/action_controller/metal/renderers.rb @@ -104,7 +104,7 @@ module ActionController # # Since <tt>ActionController::Metal</tt> controllers cannot render, the controller # must include <tt>AbstractController::Rendering</tt>, <tt>ActionController::Rendering</tt>, - # and <tt>ActionController::Renderers</tt>, and have at lest one renderer. + # and <tt>ActionController::Renderers</tt>, and have at least one renderer. # # Rather than including <tt>ActionController::Renderers::All</tt> and including all renderers, # you may specify which renderers to include by passing the renderer name or names to diff --git a/actionpack/lib/action_controller/metal/rendering.rb b/actionpack/lib/action_controller/metal/rendering.rb index 56cfb4fbba..6b17719381 100644 --- a/actionpack/lib/action_controller/metal/rendering.rb +++ b/actionpack/lib/action_controller/metal/rendering.rb @@ -54,6 +54,12 @@ module ActionController private + def _process_variant(options) + if defined?(request) && !request.nil? && request.variant.present? + options[:variant] = request.variant + end + end + def _render_in_priorities(options) RENDER_FORMATS_IN_PRIORITY.each do |format| return options[format] if options.key?(format) @@ -73,14 +79,14 @@ module ActionController end # Normalize arguments by catching blocks and setting them on :update. - def _normalize_args(action = nil, options = {}, &blk) #:nodoc: + def _normalize_args(action = nil, options = {}, &blk) options = super options[:update] = blk if block_given? options end # Normalize both text and status options. - def _normalize_options(options) #:nodoc: + def _normalize_options(options) _normalize_text(options) if options[:html] @@ -103,12 +109,12 @@ module ActionController end # Process controller specific options, as status, content-type and location. - def _process_options(options) #:nodoc: + def _process_options(options) status, content_type, location = options.values_at(:status, :content_type, :location) self.status = status if status self.content_type = content_type if content_type - self.headers["Location"] = url_for(location) if location + headers["Location"] = url_for(location) if location super end diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb index 3d3c121280..e8965a6561 100644 --- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb +++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb @@ -152,7 +152,7 @@ module ActionController #:nodoc: request.cookie_jar = NullCookieJar.build(request, {}) end - protected + private class NullSessionHash < Rack::Session::Abstract::SessionHash #:nodoc: def initialize(req) @@ -197,7 +197,7 @@ module ActionController #:nodoc: end end - protected + private # The actual before_action that is used to verify the CSRF token. # Don't override this directly. Provide your own forgery protection # strategy instead. If you override, you'll disable same-origin @@ -208,7 +208,7 @@ module ActionController #:nodoc: # enabled on an action, this before_action flags its after_action to # verify that JavaScript responses are for XHR requests, ensuring they # follow the browser's same-origin policy. - def verify_authenticity_token + def verify_authenticity_token # :doc: mark_for_same_origin_verification! if !verified_request? @@ -219,7 +219,7 @@ module ActionController #:nodoc: end end - def handle_unverified_request + def handle_unverified_request # :doc: forgery_protection_strategy.new(self).handle_unverified_request end @@ -233,7 +233,7 @@ module ActionController #:nodoc: # If `verify_authenticity_token` was run (indicating that we have # forgery protection enabled for this request) then also verify that # we aren't serving an unauthorized cross-origin response. - def verify_same_origin_request + def verify_same_origin_request # :doc: if marked_for_same_origin_verification? && non_xhr_javascript_response? if logger && log_warning_on_csrf_failure logger.warn CROSS_ORIGIN_JAVASCRIPT_WARNING @@ -243,18 +243,18 @@ module ActionController #:nodoc: end # GET requests are checked for cross-origin JavaScript after rendering. - def mark_for_same_origin_verification! + def mark_for_same_origin_verification! # :doc: @marked_for_same_origin_verification = request.get? end # If the `verify_authenticity_token` before_action ran, verify that # JavaScript responses are only served to same-origin GET requests. - def marked_for_same_origin_verification? + def marked_for_same_origin_verification? # :doc: @marked_for_same_origin_verification ||= false end # Check for cross-origin JavaScript responses. - def non_xhr_javascript_response? + def non_xhr_javascript_response? # :doc: content_type =~ %r(\Atext/javascript) && !request.xhr? end @@ -265,20 +265,20 @@ module ActionController #:nodoc: # * Is it a GET or HEAD request? Gets should be safe and idempotent # * Does the form_authenticity_token match the given token value from the params? # * Does the X-CSRF-Token header match the form_authenticity_token - def verified_request? + def verified_request? # :doc: !protect_against_forgery? || request.get? || request.head? || (valid_request_origin? && any_authenticity_token_valid?) end # Checks if any of the authenticity tokens from the request are valid. - def any_authenticity_token_valid? + def any_authenticity_token_valid? # :doc: request_authenticity_tokens.any? do |token| valid_authenticity_token?(session, token) end end # Possible authenticity tokens sent in the request. - def request_authenticity_tokens + def request_authenticity_tokens # :doc: [form_authenticity_param, request.x_csrf_token] end @@ -290,7 +290,7 @@ module ActionController #:nodoc: # Creates a masked version of the authenticity token that varies # on each request. The masking is used to mitigate SSL attacks # like BREACH. - def masked_authenticity_token(session, form_options: {}) + def masked_authenticity_token(session, form_options: {}) # :doc: action, method = form_options.values_at(:action, :method) raw_token = if per_form_csrf_tokens && action && method @@ -309,7 +309,7 @@ module ActionController #:nodoc: # Checks the client's masked token to see if it matches the # session token. Essentially the inverse of # +masked_authenticity_token+. - def valid_authenticity_token?(session, encoded_masked_token) + def valid_authenticity_token?(session, encoded_masked_token) # :doc: if encoded_masked_token.nil? || encoded_masked_token.empty? || !encoded_masked_token.is_a?(String) return false end @@ -340,7 +340,7 @@ module ActionController #:nodoc: end end - def unmask_token(masked_token) + def unmask_token(masked_token) # :doc: # Split the token into the one-time pad and the encrypted # value and decrypt it one_time_pad = masked_token[0...AUTHENTICITY_TOKEN_LENGTH] @@ -348,11 +348,11 @@ module ActionController #:nodoc: xor_byte_strings(one_time_pad, encrypted_csrf_token) end - def compare_with_real_token(token, session) + def compare_with_real_token(token, session) # :doc: ActiveSupport::SecurityUtils.secure_compare(token, real_csrf_token(session)) end - def valid_per_form_csrf_token?(token, session) + def valid_per_form_csrf_token?(token, session) # :doc: if per_form_csrf_tokens correct_token = per_form_csrf_token( session, @@ -366,12 +366,12 @@ module ActionController #:nodoc: end end - def real_csrf_token(session) + def real_csrf_token(session) # :doc: session[:_csrf_token] ||= SecureRandom.base64(AUTHENTICITY_TOKEN_LENGTH) Base64.strict_decode64(session[:_csrf_token]) end - def per_form_csrf_token(session, action_path, method) + def per_form_csrf_token(session, action_path, method) # :doc: OpenSSL::HMAC.digest( OpenSSL::Digest::SHA256.new, real_csrf_token(session), @@ -379,25 +379,25 @@ module ActionController #:nodoc: ) end - def xor_byte_strings(s1, s2) + def xor_byte_strings(s1, s2) # :doc: s2_bytes = s2.bytes s1.each_byte.with_index { |c1, i| s2_bytes[i] ^= c1 } s2_bytes.pack("C*") end # The form's authenticity parameter. Override to provide your own. - def form_authenticity_param + def form_authenticity_param # :doc: params[request_forgery_protection_token] end # Checks if the controller allows forgery protection. - def protect_against_forgery? + def protect_against_forgery? # :doc: allow_forgery_protection end # Checks if the request originated from the same origin by looking at the # Origin header. - def valid_request_origin? + def valid_request_origin? # :doc: if forgery_protection_origin_check # We accept blank origin headers because some user agents don't send it. request.origin.nil? || request.origin == request.base_url @@ -406,7 +406,7 @@ module ActionController #:nodoc: end end - def normalize_action_path(action_path) + def normalize_action_path(action_path) # :doc: uri = URI.parse(action_path) uri.path.chomp("/") end diff --git a/actionpack/lib/action_controller/metal/streaming.rb b/actionpack/lib/action_controller/metal/streaming.rb index 481f19f1ef..877a08b222 100644 --- a/actionpack/lib/action_controller/metal/streaming.rb +++ b/actionpack/lib/action_controller/metal/streaming.rb @@ -193,10 +193,10 @@ module ActionController #:nodoc: module Streaming extend ActiveSupport::Concern - protected + private # Set proper cache control and transfer encoding when streaming - def _process_options(options) #:nodoc: + def _process_options(options) super if options[:stream] if request.version == "HTTP/1.0" @@ -210,7 +210,7 @@ module ActionController #:nodoc: end # Call render_body if we are streaming instead of usual +render+. - def _render_template(options) #:nodoc: + def _render_template(options) if options.delete(:stream) Rack::Chunked::Body.new view_renderer.render_body(view_context, options) else diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb index acfeca1fcb..6e8df02fb9 100644 --- a/actionpack/lib/action_controller/metal/strong_parameters.rb +++ b/actionpack/lib/action_controller/metal/strong_parameters.rb @@ -150,7 +150,7 @@ module ActionController # permitted flag. def ==(other) if other.respond_to?(:permitted?) - self.permitted? == other.permitted? && self.parameters == other.parameters + permitted? == other.permitted? && parameters == other.parameters else @parameters == other end diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb index c639178776..57dd605b51 100644 --- a/actionpack/lib/action_controller/test_case.rb +++ b/actionpack/lib/action_controller/test_case.rb @@ -354,7 +354,7 @@ module ActionController end def controller_class - if current_controller_class = self._controller_class + if current_controller_class = _controller_class current_controller_class else self.controller_class = determine_default_controller_class(name) @@ -389,9 +389,7 @@ module ActionController # Note that the request method is not verified. The different methods are # available to make the tests more expressive. def get(action, **args) - res = process(action, method: "GET", **args) - cookies.update res.cookies - res + process(action, method: "GET", **args) end # Simulate a POST request with the given parameters and set/volley the response. @@ -515,12 +513,11 @@ module ActionController @request = @controller.request @response = @controller.response - @request.delete_header "HTTP_COOKIE" - if @request.have_cookie_jar? unless @request.cookie_jar.committed? @request.cookie_jar.write(@response) cookies.update(@request.cookie_jar.instance_variable_get(:@cookies)) + cookies.update(@response.cookies) end end @response.prepare! diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb index 7931e53c3e..028177ace2 100644 --- a/actionpack/lib/action_dispatch.rb +++ b/actionpack/lib/action_dispatch.rb @@ -1,5 +1,5 @@ #-- -# Copyright (c) 2004-2016 David Heinemeier Hansson +# Copyright (c) 2004-2017 David Heinemeier Hansson # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the diff --git a/actionpack/lib/action_dispatch/http/filter_parameters.rb b/actionpack/lib/action_dispatch/http/filter_parameters.rb index e5874a39f6..e584b84d92 100644 --- a/actionpack/lib/action_dispatch/http/filter_parameters.rb +++ b/actionpack/lib/action_dispatch/http/filter_parameters.rb @@ -51,28 +51,28 @@ module ActionDispatch @filtered_path ||= query_string.empty? ? path : "#{path}?#{filtered_query_string}" end - protected + private - def parameter_filter + def parameter_filter # :doc: parameter_filter_for fetch_header("action_dispatch.parameter_filter") { return NULL_PARAM_FILTER } end - def env_filter + def env_filter # :doc: user_key = fetch_header("action_dispatch.parameter_filter") { return NULL_ENV_FILTER } parameter_filter_for(Array(user_key) + ENV_MATCH) end - def parameter_filter_for(filters) + def parameter_filter_for(filters) # :doc: ParameterFilter.new(filters) end KV_RE = "[^&;=]+" PAIR_RE = %r{(#{KV_RE})=(#{KV_RE})} - def filtered_query_string + def filtered_query_string # :doc: query_string.gsub(PAIR_RE) do |_| parameter_filter.filter([[$1, $2]]).first.join("=") end diff --git a/actionpack/lib/action_dispatch/http/mime_negotiation.rb b/actionpack/lib/action_dispatch/http/mime_negotiation.rb index e5f20003a3..c4fe3a5c09 100644 --- a/actionpack/lib/action_dispatch/http/mime_negotiation.rb +++ b/actionpack/lib/action_dispatch/http/mime_negotiation.rb @@ -150,20 +150,20 @@ module ActionDispatch order.include?(Mime::ALL) ? format : nil end - protected + private BROWSER_LIKE_ACCEPTS = /,\s*\*\/\*|\*\/\*\s*,/ - def valid_accept_header + def valid_accept_header # :doc: (xhr? && (accept.present? || content_mime_type)) || (accept.present? && accept !~ BROWSER_LIKE_ACCEPTS) end - def use_accept_header + def use_accept_header # :doc: !self.class.ignore_accept_header end - def format_from_path_extension + def format_from_path_extension # :doc: path = get_header("action_dispatch.original_path") || get_header("PATH_INFO") if match = path && path.match(/\.(\w+)\z/) Mime[match.captures.first] diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb index 58eb8d0baf..6b718e3682 100644 --- a/actionpack/lib/action_dispatch/http/mime_type.rb +++ b/actionpack/lib/action_dispatch/http/mime_type.rb @@ -278,6 +278,8 @@ module Mime def all?; false; end + # TODO Change this to private once we've dropped Ruby 2.2 support. + # Workaround for Ruby 2.2 "private attribute?" warning. protected attr_reader :string, :synonyms @@ -295,7 +297,7 @@ module Mime end end - def respond_to_missing?(method, include_private = false) #:nodoc: + def respond_to_missing?(method, include_private = false) method.to_s.ends_with? "?" end end diff --git a/actionpack/lib/action_dispatch/http/parameters.rb b/actionpack/lib/action_dispatch/http/parameters.rb index ddd15b748b..8f21eca440 100644 --- a/actionpack/lib/action_dispatch/http/parameters.rb +++ b/actionpack/lib/action_dispatch/http/parameters.rb @@ -22,6 +22,7 @@ module ActionDispatch included do class << self + # Returns the parameter parsers. attr_reader :parameter_parsers end @@ -29,7 +30,16 @@ module ActionDispatch end module ClassMethods - def parameter_parsers=(parsers) # :nodoc: + # Configure the parameter parser for a given mime type. + # + # It accepts a hash where the key is the symbol of the mime type + # and the value is a proc. + # + # original_parsers = ActionDispatch::Request.parameter_parsers + # xml_parser = -> (raw_post) { Hash.from_xml(raw_post) || {} } + # new_parsers = original_parsers.merge(xml: xml_parser) + # ActionDispatch::Request.parameter_parsers = new_parsers + def parameter_parsers=(parsers) @parameter_parsers = parsers.transform_keys { |key| key.respond_to?(:symbol) ? key.symbol : key } end end @@ -45,7 +55,7 @@ module ActionDispatch query_parameters.dup end params.merge!(path_parameters) - params = set_custom_encoding(params) + params = set_binary_encoding(params) set_header("action_dispatch.request.parameters", params) params end @@ -73,21 +83,16 @@ module ActionDispatch private - def set_custom_encoding(params) + def set_binary_encoding(params) action = params[:action] - params.each do |k, v| - if v.is_a?(String) && v.encoding != encoding_template(action, k) - params[k] = v.force_encoding(encoding_template(action, k)) + if controller_class.binary_params_for?(action) + ActionDispatch::Request::Utils.each_param_value(params) do |param| + param.force_encoding ::Encoding::ASCII_8BIT end end - params end - def encoding_template(action, param) - controller_class.encoding_for_param(action, param) - end - def parse_formatted_parameters(parsers) return yield if content_length.zero? || content_mime_type.nil? diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb index 9986d6e1e9..19fa42ce12 100644 --- a/actionpack/lib/action_dispatch/http/request.rb +++ b/actionpack/lib/action_dispatch/http/request.rb @@ -69,7 +69,7 @@ module ActionDispatch PASS_NOT_FOUND = Class.new { # :nodoc: def self.action(_); self; end def self.call(_); [404, { "X-Cascade" => "pass" }, []]; end - def self.encoding_for_param(action, param); ::Encoding::UTF_8; end + def self.binary_params_for?(action); false; end } def controller_class @@ -85,6 +85,9 @@ module ActionDispatch end end + # Returns true if the request has a header matching the given key parameter. + # + # request.key? :ip_spoofing_check # => true def key?(key) has_header? key end diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb index f71c6afd6c..dc159596c4 100644 --- a/actionpack/lib/action_dispatch/http/response.rb +++ b/actionpack/lib/action_dispatch/http/response.rb @@ -227,7 +227,9 @@ module ActionDispatch # :nodoc: return unless content_type new_header_info = parse_content_type(content_type.to_s) prev_header_info = parsed_content_type_header - set_content_type new_header_info.mime_type, new_header_info.charset || prev_header_info.charset || self.class.default_charset + charset = new_header_info.charset || prev_header_info.charset + charset ||= self.class.default_charset unless prev_header_info.mime_type + set_content_type new_header_info.mime_type, charset end # Sets the HTTP response's content MIME type. For example, in the controller @@ -423,7 +425,7 @@ module ActionDispatch # :nodoc: def set_content_type(content_type, charset) type = (content_type || "").dup - type << "; charset=#{charset}" if charset + type << "; charset=#{charset.to_s.downcase}" if charset set_header CONTENT_TYPE, type end diff --git a/actionpack/lib/action_dispatch/journey/formatter.rb b/actionpack/lib/action_dispatch/journey/formatter.rb index 20ff4441a0..f3b8e82d32 100644 --- a/actionpack/lib/action_dispatch/journey/formatter.rb +++ b/actionpack/lib/action_dispatch/journey/formatter.rb @@ -36,7 +36,7 @@ module ActionDispatch route.parts.reverse_each do |key| break if defaults[key].nil? && parameterized_parts[key].present? - break if parameterized_parts[key].to_s != defaults[key].to_s + next if parameterized_parts[key].to_s != defaults[key].to_s break if required_parts.include?(key) parameterized_parts.delete(key) @@ -92,7 +92,11 @@ module ActionDispatch else routes = non_recursive(cache, options) - hash = routes.group_by { |_, r| r.score(options) } + supplied_keys = options.each_with_object({}) do |(k, v), h| + h[k.to_s] = true if v + end + + hash = routes.group_by { |_, r| r.score(supplied_keys) } hash.keys.sort.reverse_each do |score| break if score < 0 diff --git a/actionpack/lib/action_dispatch/journey/parser.rb b/actionpack/lib/action_dispatch/journey/parser.rb index ee91b11b42..db42b64c4b 100644 --- a/actionpack/lib/action_dispatch/journey/parser.rb +++ b/actionpack/lib/action_dispatch/journey/parser.rb @@ -1,198 +1,199 @@ # # DO NOT MODIFY!!!! -# This file is automatically generated by Racc 1.4.11 +# This file is automatically generated by Racc 1.4.14 # from Racc grammer file "". # -require "racc/parser.rb" +require 'racc/parser.rb' + +# :stopdoc: require "action_dispatch/journey/parser_extras" module ActionDispatch - # :stopdoc: module Journey class Parser < Racc::Parser - ##### State transition tables begin ### - - racc_action_table = [ - 13, 15, 14, 7, 21, 16, 8, 19, 13, 15, - 14, 7, 17, 16, 8, 13, 15, 14, 7, 24, - 16, 8, 13, 15, 14, 7, 19, 16, 8 ] - - racc_action_check = [ - 2, 2, 2, 2, 17, 2, 2, 2, 0, 0, - 0, 0, 1, 0, 0, 19, 19, 19, 19, 20, - 19, 19, 7, 7, 7, 7, 22, 7, 7 ] - - racc_action_pointer = [ - 6, 12, -2, nil, nil, nil, nil, 20, nil, nil, - nil, nil, nil, nil, nil, nil, nil, 4, nil, 13, - 13, nil, 17, nil, nil ] - - racc_action_default = [ - -19, -19, -2, -3, -4, -5, -6, -19, -10, -11, - -12, -13, -14, -15, -16, -17, -18, -19, -1, -19, - -19, 25, -8, -9, -7 ] - - racc_goto_table = [ - 1, 22, 18, 23, nil, nil, nil, 20 ] - - racc_goto_check = [ - 1, 2, 1, 3, nil, nil, nil, 1 ] - - racc_goto_pointer = [ - nil, 0, -18, -16, nil, nil, nil, nil, nil, nil, - nil ] - - racc_goto_default = [ - nil, nil, 2, 3, 4, 5, 6, 9, 10, 11, - 12 ] - - racc_reduce_table = [ - 0, 0, :racc_error, - 2, 11, :_reduce_1, - 1, 11, :_reduce_2, - 1, 11, :_reduce_none, - 1, 12, :_reduce_none, - 1, 12, :_reduce_none, - 1, 12, :_reduce_none, - 3, 15, :_reduce_7, - 3, 13, :_reduce_8, - 3, 13, :_reduce_9, - 1, 16, :_reduce_10, - 1, 14, :_reduce_none, - 1, 14, :_reduce_none, - 1, 14, :_reduce_none, - 1, 14, :_reduce_none, - 1, 19, :_reduce_15, - 1, 17, :_reduce_16, - 1, 18, :_reduce_17, - 1, 20, :_reduce_18 ] - - racc_reduce_n = 19 - - racc_shift_n = 25 - - racc_token_table = { - false => 0, - :error => 1, - :SLASH => 2, - :LITERAL => 3, - :SYMBOL => 4, - :LPAREN => 5, - :RPAREN => 6, - :DOT => 7, - :STAR => 8, - :OR => 9 } - - racc_nt_base = 10 - - racc_use_result_var = false - - Racc_arg = [ - racc_action_table, - racc_action_check, - racc_action_default, - racc_action_pointer, - racc_goto_table, - racc_goto_check, - racc_goto_default, - racc_goto_pointer, - racc_nt_base, - racc_reduce_table, - racc_token_table, - racc_shift_n, - racc_reduce_n, - racc_use_result_var ] - - Racc_token_to_s_table = [ - "$end", - "error", - "SLASH", - "LITERAL", - "SYMBOL", - "LPAREN", - "RPAREN", - "DOT", - "STAR", - "OR", - "$start", - "expressions", - "expression", - "or", - "terminal", - "group", - "star", - "symbol", - "literal", - "slash", - "dot" ] - - Racc_debug_parser = false - - ##### State transition tables end ##### - - # reduce 0 omitted - - def _reduce_1(val, _values) - Cat.new(val.first, val.last) - end - - def _reduce_2(val, _values) - val.first - end - - # reduce 3 omitted - - # reduce 4 omitted - - # reduce 5 omitted - - # reduce 6 omitted - - def _reduce_7(val, _values) - Group.new(val[1]) - end - - def _reduce_8(val, _values) - Or.new([val.first, val.last]) - end - - def _reduce_9(val, _values) - Or.new([val.first, val.last]) - end - - def _reduce_10(val, _values) - Star.new(Symbol.new(val.last)) - end - - # reduce 11 omitted - - # reduce 12 omitted - - # reduce 13 omitted - - # reduce 14 omitted - - def _reduce_15(val, _values) - Slash.new("/") - end - - def _reduce_16(val, _values) - Symbol.new(val.first) - end - - def _reduce_17(val, _values) - Literal.new(val.first) - end - - def _reduce_18(val, _values) - Dot.new(val.first) - end - - def _reduce_none(val, _values) - val[0] - end +##### State transition tables begin ### + +racc_action_table = [ + 13, 15, 14, 7, 19, 16, 8, 19, 13, 15, + 14, 7, 17, 16, 8, 13, 15, 14, 7, 21, + 16, 8, 13, 15, 14, 7, 24, 16, 8 ] + +racc_action_check = [ + 2, 2, 2, 2, 22, 2, 2, 2, 19, 19, + 19, 19, 1, 19, 19, 7, 7, 7, 7, 17, + 7, 7, 0, 0, 0, 0, 20, 0, 0 ] + +racc_action_pointer = [ + 20, 12, -2, nil, nil, nil, nil, 13, nil, nil, + nil, nil, nil, nil, nil, nil, nil, 19, nil, 6, + 20, nil, -5, nil, nil ] + +racc_action_default = [ + -19, -19, -2, -3, -4, -5, -6, -19, -10, -11, + -12, -13, -14, -15, -16, -17, -18, -19, -1, -19, + -19, 25, -8, -9, -7 ] + +racc_goto_table = [ + 1, 22, 18, 23, nil, nil, nil, 20 ] + +racc_goto_check = [ + 1, 2, 1, 3, nil, nil, nil, 1 ] + +racc_goto_pointer = [ + nil, 0, -18, -16, nil, nil, nil, nil, nil, nil, + nil ] + +racc_goto_default = [ + nil, nil, 2, 3, 4, 5, 6, 9, 10, 11, + 12 ] + +racc_reduce_table = [ + 0, 0, :racc_error, + 2, 11, :_reduce_1, + 1, 11, :_reduce_2, + 1, 11, :_reduce_none, + 1, 12, :_reduce_none, + 1, 12, :_reduce_none, + 1, 12, :_reduce_none, + 3, 15, :_reduce_7, + 3, 13, :_reduce_8, + 3, 13, :_reduce_9, + 1, 16, :_reduce_10, + 1, 14, :_reduce_none, + 1, 14, :_reduce_none, + 1, 14, :_reduce_none, + 1, 14, :_reduce_none, + 1, 19, :_reduce_15, + 1, 17, :_reduce_16, + 1, 18, :_reduce_17, + 1, 20, :_reduce_18 ] + +racc_reduce_n = 19 + +racc_shift_n = 25 + +racc_token_table = { + false => 0, + :error => 1, + :SLASH => 2, + :LITERAL => 3, + :SYMBOL => 4, + :LPAREN => 5, + :RPAREN => 6, + :DOT => 7, + :STAR => 8, + :OR => 9 } + +racc_nt_base = 10 + +racc_use_result_var = false + +Racc_arg = [ + racc_action_table, + racc_action_check, + racc_action_default, + racc_action_pointer, + racc_goto_table, + racc_goto_check, + racc_goto_default, + racc_goto_pointer, + racc_nt_base, + racc_reduce_table, + racc_token_table, + racc_shift_n, + racc_reduce_n, + racc_use_result_var ] + +Racc_token_to_s_table = [ + "$end", + "error", + "SLASH", + "LITERAL", + "SYMBOL", + "LPAREN", + "RPAREN", + "DOT", + "STAR", + "OR", + "$start", + "expressions", + "expression", + "or", + "terminal", + "group", + "star", + "symbol", + "literal", + "slash", + "dot" ] + +Racc_debug_parser = false + +##### State transition tables end ##### + +# reduce 0 omitted + +def _reduce_1(val, _values) + Cat.new(val.first, val.last) +end + +def _reduce_2(val, _values) + val.first +end + +# reduce 3 omitted + +# reduce 4 omitted + +# reduce 5 omitted + +# reduce 6 omitted + +def _reduce_7(val, _values) + Group.new(val[1]) +end + +def _reduce_8(val, _values) + Or.new([val.first, val.last]) +end + +def _reduce_9(val, _values) + Or.new([val.first, val.last]) +end + +def _reduce_10(val, _values) + Star.new(Symbol.new(val.last)) +end + +# reduce 11 omitted + +# reduce 12 omitted + +# reduce 13 omitted + +# reduce 14 omitted + +def _reduce_15(val, _values) + Slash.new(val.first) +end + +def _reduce_16(val, _values) + Symbol.new(val.first) +end + +def _reduce_17(val, _values) + Literal.new(val.first) +end + +def _reduce_18(val, _values) + Dot.new(val.first) +end + +def _reduce_none(val, _values) + val[0] +end + end # class Parser - end # module Journey - # :startdoc: -end # module ActionDispatch + end # module Journey + end # module ActionDispatch diff --git a/actionpack/lib/action_dispatch/journey/parser.y b/actionpack/lib/action_dispatch/journey/parser.y index d3f7c4d765..f9b1a7a958 100644 --- a/actionpack/lib/action_dispatch/journey/parser.y +++ b/actionpack/lib/action_dispatch/journey/parser.y @@ -30,7 +30,7 @@ rule | dot ; slash - : SLASH { Slash.new('/') } + : SLASH { Slash.new(val.first) } ; symbol : SYMBOL { Symbol.new(val.first) } @@ -45,5 +45,6 @@ rule end ---- header +# :stopdoc: -require 'action_dispatch/journey/parser_extras' +require "action_dispatch/journey/parser_extras" diff --git a/actionpack/lib/action_dispatch/journey/route.rb b/actionpack/lib/action_dispatch/journey/route.rb index 0cc8d83ac8..f2ac4818d8 100644 --- a/actionpack/lib/action_dispatch/journey/route.rb +++ b/actionpack/lib/action_dispatch/journey/route.rb @@ -96,13 +96,18 @@ module ActionDispatch required_parts + required_defaults.keys end - def score(constraints) + def score(supplied_keys) required_keys = path.required_names - supplied_keys = constraints.map { |k, v| v && k.to_s }.compact - return -1 unless (required_keys - supplied_keys).empty? + required_keys.each do |k| + return -1 unless supplied_keys.include?(k) + end + + score = 0 + path.names.each do |k| + score += 1 if supplied_keys.include?(k) + end - score = (supplied_keys & path.names).length score + (required_defaults.length * 2) end diff --git a/actionpack/lib/action_dispatch/journey/router/utils.rb b/actionpack/lib/action_dispatch/journey/router/utils.rb index ce5d350763..d641642338 100644 --- a/actionpack/lib/action_dispatch/journey/router/utils.rb +++ b/actionpack/lib/action_dispatch/journey/router/utils.rb @@ -58,12 +58,12 @@ module ActionDispatch uri.gsub(ESCAPED) { |match| [match[1, 2].hex].pack("C") }.force_encoding(encoding) end - protected - def escape(component, pattern) + private + def escape(component, pattern) # :doc: component.gsub(pattern) { |unsafe| percent_encode(unsafe) }.force_encoding(US_ASCII) end - def percent_encode(unsafe) + def percent_encode(unsafe) # :doc: safe = EMPTY.dup unsafe.each_byte { |b| safe << DEC2HEX[b] } safe diff --git a/actionpack/lib/action_dispatch/journey/scanner.rb b/actionpack/lib/action_dispatch/journey/scanner.rb index 4b8c8ab063..7dbb39b26d 100644 --- a/actionpack/lib/action_dispatch/journey/scanner.rb +++ b/actionpack/lib/action_dispatch/journey/scanner.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: true require "strscan" module ActionDispatch @@ -35,22 +36,23 @@ module ActionDispatch def scan case # / - when text = @ss.scan(/\//) - [:SLASH, text] + when @ss.skip(/\//) + [:SLASH, "/"] + when @ss.skip(/\(/) + [:LPAREN, "("] + when @ss.skip(/\)/) + [:RPAREN, ")"] + when @ss.skip(/\|/) + [:OR, "|"] + when @ss.skip(/\./) + [:DOT, "."] + when text = @ss.scan(/:\w+/) + [:SYMBOL, text] when text = @ss.scan(/\*\w+/) [:STAR, text] - when text = @ss.scan(/(?<!\\)\(/) - [:LPAREN, text] - when text = @ss.scan(/(?<!\\)\)/) - [:RPAREN, text] - when text = @ss.scan(/\|/) - [:OR, text] - when text = @ss.scan(/\./) - [:DOT, text] - when text = @ss.scan(/(?<!\\):\w+/) - [:SYMBOL, text] - when text = @ss.scan(/(?:[\w%\-~!$&'*+,;=@]|\\:|\\\(|\\\))+/) - [:LITERAL, text.tr('\\', "")] + when text = @ss.scan(/(?:[\w%\-~!$&'*+,;=@]|\\[:()])+/) + text.tr! "\\", "" + [:LITERAL, text] # any char when text = @ss.scan(/./) [:LITERAL, text] diff --git a/actionpack/lib/action_dispatch/middleware/flash.rb b/actionpack/lib/action_dispatch/middleware/flash.rb index 6dddcc6ee1..cbe2f4be4d 100644 --- a/actionpack/lib/action_dispatch/middleware/flash.rb +++ b/actionpack/lib/action_dispatch/middleware/flash.rb @@ -281,7 +281,8 @@ module ActionDispatch @now end - def stringify_array(array) + private + def stringify_array(array) # :doc: array.map do |item| item.kind_of?(Symbol) ? item.to_s : item end diff --git a/actionpack/lib/action_dispatch/middleware/remote_ip.rb b/actionpack/lib/action_dispatch/middleware/remote_ip.rb index 523eeb5b05..9f1ae80b97 100644 --- a/actionpack/lib/action_dispatch/middleware/remote_ip.rb +++ b/actionpack/lib/action_dispatch/middleware/remote_ip.rb @@ -153,9 +153,9 @@ module ActionDispatch @ip ||= calculate_ip end - protected + private - def ips_from(header) + def ips_from(header) # :doc: return [] unless header # Split the comma-separated list into an array of strings ips = header.strip.split(/[,\s]+/) @@ -171,7 +171,7 @@ module ActionDispatch end end - def filter_proxies(ips) + def filter_proxies(ips) # :doc: ips.reject do |ip| @proxies.any? { |proxy| proxy === ip } end diff --git a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb index 49b82e7128..97c937b0b1 100644 --- a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb @@ -27,17 +27,16 @@ module ActionDispatch sid end - protected + private - def initialize_sid + def initialize_sid # :doc: @default_options.delete(:sidbits) @default_options.delete(:secure_random) end - private - def make_request(env) - ActionDispatch::Request.new env - end + def make_request(env) + ActionDispatch::Request.new env + end end module StaleSessionCheck diff --git a/actionpack/lib/action_dispatch/middleware/static.rb b/actionpack/lib/action_dispatch/middleware/static.rb index 5c71f0fc48..5d10129d21 100644 --- a/actionpack/lib/action_dispatch/middleware/static.rb +++ b/actionpack/lib/action_dispatch/middleware/static.rb @@ -33,7 +33,7 @@ module ActionDispatch paths = [path, "#{path}#{ext}", "#{path}/#{@index}#{ext}"] if match = paths.detect { |p| - path = File.join(@root, p.force_encoding("UTF-8".freeze)) + path = File.join(@root, p.force_encoding(Encoding::UTF_8)) begin File.file?(path) && File.readable?(path) rescue SystemCallError diff --git a/actionpack/lib/action_dispatch/request/utils.rb b/actionpack/lib/action_dispatch/request/utils.rb index 282bdbd2be..01bc871e5f 100644 --- a/actionpack/lib/action_dispatch/request/utils.rb +++ b/actionpack/lib/action_dispatch/request/utils.rb @@ -4,6 +4,17 @@ module ActionDispatch mattr_accessor :perform_deep_munge self.perform_deep_munge = true + def self.each_param_value(params, &block) + case params + when Array + params.each { |element| each_param_value(element, &block) } + when Hash + params.each_value { |value| each_param_value(value, &block) } + when String + block.call params + end + end + def self.normalize_encode_params(params) if perform_deep_munge NoNilParamEncoder.normalize_encode_params params diff --git a/actionpack/lib/action_dispatch/routing.rb b/actionpack/lib/action_dispatch/routing.rb index fe8bf6a35c..c554ce98bc 100644 --- a/actionpack/lib/action_dispatch/routing.rb +++ b/actionpack/lib/action_dispatch/routing.rb @@ -1,4 +1,4 @@ -require 'active_support/core_ext/string/filters' +require "active_support/core_ext/string/filters" module ActionDispatch # The routing module provides URL rewriting in native Ruby. It's a way to diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 4efde09b8b..83652bd34c 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -238,7 +238,7 @@ module ActionDispatch options[:controller] ||= /.+?/ end - if to.respond_to? :call + if to.respond_to?(:action) || to.respond_to?(:call) options else to_endpoint = split_to to @@ -290,16 +290,14 @@ module ActionDispatch end def app(blocks) - if to.is_a?(Class) && to < ActionController::Metal + if to.respond_to?(:action) Routing::RouteSet::StaticDispatcher.new to + elsif to.respond_to?(:call) + Constraints.new(to, blocks, Constraints::CALL) + elsif blocks.any? + Constraints.new(dispatcher(defaults.key?(:controller)), blocks, Constraints::SERVE) else - if to.respond_to?(:call) - Constraints.new(to, blocks, Constraints::CALL) - elsif blocks.any? - Constraints.new(dispatcher(defaults.key?(:controller)), blocks, Constraints::SERVE) - else - dispatcher(defaults.key?(:controller)) - end + dispatcher(defaults.key?(:controller)) end end @@ -996,65 +994,65 @@ module ActionDispatch end private - def merge_path_scope(parent, child) #:nodoc: + def merge_path_scope(parent, child) Mapper.normalize_path("#{parent}/#{child}") end - def merge_shallow_path_scope(parent, child) #:nodoc: + def merge_shallow_path_scope(parent, child) Mapper.normalize_path("#{parent}/#{child}") end - def merge_as_scope(parent, child) #:nodoc: + def merge_as_scope(parent, child) parent ? "#{parent}_#{child}" : child end - def merge_shallow_prefix_scope(parent, child) #:nodoc: + def merge_shallow_prefix_scope(parent, child) parent ? "#{parent}_#{child}" : child end - def merge_module_scope(parent, child) #:nodoc: + def merge_module_scope(parent, child) parent ? "#{parent}/#{child}" : child end - def merge_controller_scope(parent, child) #:nodoc: + def merge_controller_scope(parent, child) child end - def merge_action_scope(parent, child) #:nodoc: + def merge_action_scope(parent, child) child end - def merge_via_scope(parent, child) #:nodoc: + def merge_via_scope(parent, child) child end - def merge_format_scope(parent, child) #:nodoc: + def merge_format_scope(parent, child) child end - def merge_path_names_scope(parent, child) #:nodoc: + def merge_path_names_scope(parent, child) merge_options_scope(parent, child) end - def merge_constraints_scope(parent, child) #:nodoc: + def merge_constraints_scope(parent, child) merge_options_scope(parent, child) end - def merge_defaults_scope(parent, child) #:nodoc: + def merge_defaults_scope(parent, child) merge_options_scope(parent, child) end - def merge_blocks_scope(parent, child) #:nodoc: + def merge_blocks_scope(parent, child) merged = parent ? parent.dup : [] merged << child if child merged end - def merge_options_scope(parent, child) #:nodoc: + def merge_options_scope(parent, child) (parent || {}).merge(child) end - def merge_shallow_scope(parent, child) #:nodoc: + def merge_shallow_scope(parent, child) child ? true : false end @@ -1619,13 +1617,13 @@ module ActionDispatch end end - protected + private - def parent_resource #:nodoc: + def parent_resource @scope[:scope_level_resource] end - def apply_common_behavior_for(method, resources, options, &block) #:nodoc: + def apply_common_behavior_for(method, resources, options, &block) if resources.length > 1 resources.each { |r| send(method, r, options, &block) } return true @@ -1658,39 +1656,39 @@ module ActionDispatch false end - def apply_action_options(options) # :nodoc: + def apply_action_options(options) return options if action_options? options options.merge scope_action_options end - def action_options?(options) #:nodoc: + def action_options?(options) options[:only] || options[:except] end - def scope_action_options #:nodoc: + def scope_action_options @scope[:action_options] || {} end - def resource_scope? #:nodoc: + def resource_scope? @scope.resource_scope? end - def resource_method_scope? #:nodoc: + def resource_method_scope? @scope.resource_method_scope? end - def nested_scope? #:nodoc: + def nested_scope? @scope.nested? end - def with_scope_level(kind) + def with_scope_level(kind) # :doc: @scope = @scope.new_level(kind) yield ensure @scope = @scope.parent end - def resource_scope(resource) #:nodoc: + def resource_scope(resource) @scope = @scope.new(scope_level_resource: resource) controller(resource.resource_scope) { yield } @@ -1698,7 +1696,7 @@ module ActionDispatch @scope = @scope.parent end - def nested_options #:nodoc: + def nested_options options = { as: parent_resource.member_name } options[:constraints] = { parent_resource.nested_param => param_constraint @@ -1707,25 +1705,25 @@ module ActionDispatch options end - def shallow_nesting_depth #:nodoc: + def shallow_nesting_depth @scope.find_all { |node| node.frame[:scope_level_resource] }.count { |node| node.frame[:scope_level_resource].shallow? } end - def param_constraint? #:nodoc: + def param_constraint? @scope[:constraints] && @scope[:constraints][parent_resource.param].is_a?(Regexp) end - def param_constraint #:nodoc: + def param_constraint @scope[:constraints][parent_resource.param] end - def canonical_action?(action) #:nodoc: + def canonical_action?(action) resource_method_scope? && CANONICAL_ACTIONS.include?(action.to_s) end - def shallow_scope #:nodoc: + def shallow_scope scope = { as: @scope[:shallow_prefix], path: @scope[:shallow_path] } @scope = @scope.new scope @@ -1735,7 +1733,7 @@ module ActionDispatch @scope = @scope.parent end - def path_for_action(action, path) #:nodoc: + def path_for_action(action, path) return "#{@scope[:path]}/#{path}" if path if canonical_action?(action) @@ -1745,11 +1743,11 @@ module ActionDispatch end end - def action_path(name) #:nodoc: + def action_path(name) @scope[:path_names][name.to_sym] || name end - def prefix_name_for_action(as, action) #:nodoc: + def prefix_name_for_action(as, action) if as prefix = as elsif !canonical_action?(action) @@ -1761,7 +1759,7 @@ module ActionDispatch end end - def name_for_action(as, action) #:nodoc: + def name_for_action(as, action) prefix = prefix_name_for_action(as, action) name_prefix = @scope[:as] @@ -1787,7 +1785,7 @@ module ActionDispatch end end - def set_member_mappings_for_resource + def set_member_mappings_for_resource # :doc: member do get :edit if parent_resource.actions.include?(:edit) get :show if parent_resource.actions.include?(:show) @@ -1799,12 +1797,10 @@ module ActionDispatch end end - def api_only? + def api_only? # :doc: @set.api_only? end - private - def path_scope(path) @scope = @scope.new(path: merge_path_scope(@scope[:path], path)) yield @@ -1868,7 +1864,7 @@ module ActionDispatch path =~ %r{^/?[-\w]+/[-\w/]+$} end - def decomposed_match(path, controller, options, _path, to, via, formatted, anchor, options_constraints) # :nodoc: + def decomposed_match(path, controller, options, _path, to, via, formatted, anchor, options_constraints) if on = options.delete(:on) send(on) { decomposed_match(path, controller, options, _path, to, via, formatted, anchor, options_constraints) } else @@ -1883,7 +1879,7 @@ module ActionDispatch end end - def add_route(action, controller, options, _path, to, via, formatted, anchor, options_constraints) # :nodoc: + def add_route(action, controller, options, _path, to, via, formatted, anchor, options_constraints) path = path_for_action(action, _path) raise ArgumentError, "path is required" if path.blank? diff --git a/actionpack/lib/action_dispatch/routing/url_for.rb b/actionpack/lib/action_dispatch/routing/url_for.rb index a1ac5a2b6c..3e564f13d8 100644 --- a/actionpack/lib/action_dispatch/routing/url_for.rb +++ b/actionpack/lib/action_dispatch/routing/url_for.rb @@ -198,14 +198,16 @@ module ActionDispatch _routes.optimize_routes_generation? && default_url_options.empty? end - def _with_routes(routes) + private + + def _with_routes(routes) # :doc: old_routes, @_routes = @_routes, routes yield ensure @_routes = old_routes end - def _routes_context + def _routes_context # :doc: self end end diff --git a/actionpack/lib/action_dispatch/testing/assertions.rb b/actionpack/lib/action_dispatch/testing/assertions.rb index b362931ef7..4ea18d671d 100644 --- a/actionpack/lib/action_dispatch/testing/assertions.rb +++ b/actionpack/lib/action_dispatch/testing/assertions.rb @@ -12,7 +12,7 @@ module ActionDispatch include Rails::Dom::Testing::Assertions def html_document - @html_document ||= if @response.content_type.to_s =~ /xml\z/ + @html_document ||= if @response.content_type.to_s.end_with?("xml") Nokogiri::XML::Document.parse(@response.body) else Nokogiri::HTML::Document.parse(@response.body) diff --git a/actionpack/lib/action_dispatch/testing/assertions/routing.rb b/actionpack/lib/action_dispatch/testing/assertions/routing.rb index 454dcb9307..37c1ca02b6 100644 --- a/actionpack/lib/action_dispatch/testing/assertions/routing.rb +++ b/actionpack/lib/action_dispatch/testing/assertions/routing.rb @@ -152,8 +152,11 @@ module ActionDispatch _routes = @routes @controller.singleton_class.include(_routes.url_helpers) - @controller.view_context_class = Class.new(@controller.view_context_class) do - include _routes.url_helpers + + if @controller.respond_to? :view_context_class + @controller.view_context_class = Class.new(@controller.view_context_class) do + include _routes.url_helpers + end end end yield @routes diff --git a/actionpack/lib/action_pack.rb b/actionpack/lib/action_pack.rb index ee6dabd133..eec622e085 100644 --- a/actionpack/lib/action_pack.rb +++ b/actionpack/lib/action_pack.rb @@ -1,5 +1,5 @@ #-- -# Copyright (c) 2004-2016 David Heinemeier Hansson +# Copyright (c) 2004-2017 David Heinemeier Hansson # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index 11a9092527..7c113426a5 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -7,8 +7,8 @@ require "active_support/core_ext/kernel/reporting" # These are the normal settings that will be set up by Railties # TODO: Have these tests support other combinations of these values silence_warnings do - Encoding.default_internal = "UTF-8" - Encoding.default_external = "UTF-8" + Encoding.default_internal = Encoding::UTF_8 + Encoding.default_external = Encoding::UTF_8 end require "drb" @@ -259,9 +259,9 @@ module ActionDispatch host = uri_or_host.host unless path path ||= uri_or_host.path - params = { "PATH_INFO" => path, - "REQUEST_METHOD" => method, - "HTTP_HOST" => host } + params = { "PATH_INFO" => path, + "REQUEST_METHOD" => method, + "HTTP_HOST" => host } routes.call(params) end diff --git a/actionpack/test/controller/action_pack_assertions_test.rb b/actionpack/test/controller/action_pack_assertions_test.rb index b08f1f1707..9ab152fc5c 100644 --- a/actionpack/test/controller/action_pack_assertions_test.rb +++ b/actionpack/test/controller/action_pack_assertions_test.rb @@ -128,6 +128,16 @@ module Admin end end +class ApiOnlyController < ActionController::API + def nothing + head :ok + end + + def redirect_to_new_route + redirect_to new_route_url + end +end + class ActionPackAssertionsControllerTest < ActionController::TestCase def test_render_file_absolute_path get :render_file_absolute_path @@ -170,6 +180,20 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase end end + def test_with_routing_works_with_api_only_controllers + @controller = ApiOnlyController.new + + with_routing do |set| + set.draw do + get "new_route", to: "api_only#nothing" + get "redirect_to_new_route", to: "api_only#redirect_to_new_route" + end + + process :redirect_to_new_route + assert_redirected_to "http://test.host/new_route" + end + end + def test_assert_redirect_to_named_route_failure with_routing do |set| set.draw do diff --git a/actionpack/test/controller/filters_test.rb b/actionpack/test/controller/filters_test.rb index e0fa1fbab8..f9701585a9 100644 --- a/actionpack/test/controller/filters_test.rb +++ b/actionpack/test/controller/filters_test.rb @@ -55,7 +55,7 @@ class FilterTest < ActionController::TestCase end end - protected + private (1..3).each do |i| define_method "try_#{i}" do instance_variable_set :@try, i @@ -296,7 +296,7 @@ class FilterTest < ActionController::TestCase render inline: "ran action" end - protected + private def find_user @ran_filter ||= [] @ran_filter << "find_user" @@ -428,7 +428,7 @@ class FilterTest < ActionController::TestCase render plain: "bar" end - protected + private def first @first = true end @@ -1040,7 +1040,7 @@ class YieldingAroundFiltersTest < ActionController::TestCase assert_equal 3, controller.instance_variable_get(:@try) end - protected + private def test_process(controller, action = "show") @controller = controller.is_a?(Class) ? controller.new : controller process(action) diff --git a/actionpack/test/controller/flash_hash_test.rb b/actionpack/test/controller/flash_hash_test.rb index 6b3abdd5be..45b598a594 100644 --- a/actionpack/test/controller/flash_hash_test.rb +++ b/actionpack/test/controller/flash_hash_test.rb @@ -63,10 +63,10 @@ module ActionDispatch assert_equal({ "flashes" => { "foo" => "bar" }, "discard" => [] }, @hash.to_session_value) @hash.discard("foo") - assert_equal(nil, @hash.to_session_value) + assert_nil(@hash.to_session_value) @hash.sweep - assert_equal(nil, @hash.to_session_value) + assert_nil(@hash.to_session_value) end def test_from_session_value @@ -75,7 +75,7 @@ module ActionDispatch session = Marshal.load(Base64.decode64(rails_3_2_cookie)) hash = Flash::FlashHash.from_session_value(session["flash"]) assert_equal({ "greeting" => "Hello" }, hash.to_hash) - assert_equal(nil, hash.to_session_value) + assert_nil(hash.to_session_value) end def test_from_session_value_on_json_serializer @@ -84,7 +84,7 @@ module ActionDispatch hash = Flash::FlashHash.from_session_value(session["flash"]) assert_equal({ "greeting" => "Hello" }, hash.to_hash) - assert_equal(nil, hash.to_session_value) + assert_nil(hash.to_session_value) assert_equal "Hello", hash[:greeting] assert_equal "Hello", hash["greeting"] end diff --git a/actionpack/test/controller/http_token_authentication_test.rb b/actionpack/test/controller/http_token_authentication_test.rb index 3842136682..09d2793c9a 100644 --- a/actionpack/test/controller/http_token_authentication_test.rb +++ b/actionpack/test/controller/http_token_authentication_test.rb @@ -166,8 +166,7 @@ class HttpTokenAuthenticationTest < ActionController::TestCase test "token_and_options returns nil with no value after the equal sign" do actual = ActionController::HttpAuthentication::Token.token_and_options(malformed_request).first - expected = nil - assert_equal(expected, actual) + assert_nil actual end test "raw_params returns a tuple of two key value pair strings" do diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb index d3aa81a0f7..cd1415b56c 100644 --- a/actionpack/test/controller/integration_test.rb +++ b/actionpack/test/controller/integration_test.rb @@ -269,8 +269,8 @@ class IntegrationProcessTest < ActionDispatch::IntegrationTest test "response cookies are added to the cookie jar for the next request" do with_test_route_set do - self.cookies["cookie_1"] = "sugar" - self.cookies["cookie_2"] = "oatmeal" + cookies["cookie_1"] = "sugar" + cookies["cookie_2"] = "oatmeal" get "/cookie_monster" assert_equal "cookie_1=; path=/\ncookie_3=chocolate; path=/", headers["Set-Cookie"] assert_equal({ "cookie_1" => "", "cookie_2" => "oatmeal", "cookie_3" => "chocolate" }, cookies.to_hash) @@ -289,7 +289,7 @@ class IntegrationProcessTest < ActionDispatch::IntegrationTest assert_response :success assert_equal "bar", body - assert_equal nil, headers["Set-Cookie"] + assert_nil headers["Set-Cookie"] assert_equal({ "foo" => "bar" }, cookies.to_hash) end end @@ -308,7 +308,7 @@ class IntegrationProcessTest < ActionDispatch::IntegrationTest assert_response :success assert_equal "bar", body - assert_equal nil, headers["Set-Cookie"] + assert_nil headers["Set-Cookie"] assert_equal({ "foo" => "bar" }, cookies.to_hash) end end diff --git a/actionpack/test/controller/mime/accept_format_test.rb b/actionpack/test/controller/mime/accept_format_test.rb index a2834c9f39..a22fa39051 100644 --- a/actionpack/test/controller/mime/accept_format_test.rb +++ b/actionpack/test/controller/mime/accept_format_test.rb @@ -40,7 +40,7 @@ class PostController < AbstractPostController respond_to(:html, :iphone, :js) end -protected +private def with_iphone request.format = "iphone" if request.env["HTTP_ACCEPT"] == "text/iphone" diff --git a/actionpack/test/controller/mime/respond_to_test.rb b/actionpack/test/controller/mime/respond_to_test.rb index c5f8165d04..818dc119eb 100644 --- a/actionpack/test/controller/mime/respond_to_test.rb +++ b/actionpack/test/controller/mime/respond_to_test.rb @@ -273,7 +273,7 @@ class RespondToController < ActionController::Base end end - protected + private def set_layout case action_name when "all_types_with_layout", "iphone_with_html_response_type" diff --git a/actionpack/test/controller/new_base/bare_metal_test.rb b/actionpack/test/controller/new_base/bare_metal_test.rb index 9956f0b107..054757fab3 100644 --- a/actionpack/test/controller/new_base/bare_metal_test.rb +++ b/actionpack/test/controller/new_base/bare_metal_test.rb @@ -52,7 +52,7 @@ module BareMetalTest controller.set_request!(ActionDispatch::Request.empty) controller.set_response!(BareController.make_response!(controller.request)) controller.index - assert_equal nil, controller.response_body + assert_nil controller.response_body end end diff --git a/actionpack/test/controller/new_base/base_test.rb b/actionpack/test/controller/new_base/base_test.rb index 3765d406fc..b891df4c0f 100644 --- a/actionpack/test/controller/new_base/base_test.rb +++ b/actionpack/test/controller/new_base/base_test.rb @@ -25,7 +25,7 @@ module Dispatching render body: "actions: #{action_methods.to_a.sort.join(', ')}" end - protected + private def authenticate end end diff --git a/actionpack/test/controller/new_base/render_context_test.rb b/actionpack/test/controller/new_base/render_context_test.rb index b64468a94e..25b73ac78c 100644 --- a/actionpack/test/controller/new_base/render_context_test.rb +++ b/actionpack/test/controller/new_base/render_context_test.rb @@ -26,16 +26,14 @@ module RenderContext render action: "hello_world", layout: "basic" end - protected - - # 3) Set view_context to self - def view_context - self - end + protected def __controller_method__ + "controller context!" + end - def __controller_method__ - "controller context!" - end + # 3) Set view_context to self + private def view_context + self + end end class RenderContextTest < Rack::TestCase diff --git a/actionpack/test/controller/new_base/render_streaming_test.rb b/actionpack/test/controller/new_base/render_streaming_test.rb index 64b799f826..1177b8b03e 100644 --- a/actionpack/test/controller/new_base/render_streaming_test.rb +++ b/actionpack/test/controller/new_base/render_streaming_test.rb @@ -101,12 +101,12 @@ module RenderStreaming assert_body "Hello world, I'm here!" assert_status 200 assert_equal "22", headers["Content-Length"] - assert_equal nil, headers["Transfer-Encoding"] + assert_nil headers["Transfer-Encoding"] end def assert_streaming!(cache = "no-cache") assert_status 200 - assert_equal nil, headers["Content-Length"] + assert_nil headers["Content-Length"] assert_equal "chunked", headers["Transfer-Encoding"] assert_equal cache, headers["Cache-Control"] end diff --git a/actionpack/test/controller/parameter_encoding_test.rb b/actionpack/test/controller/parameter_encoding_test.rb index 7840b4f5c4..234d0bddd1 100644 --- a/actionpack/test/controller/parameter_encoding_test.rb +++ b/actionpack/test/controller/parameter_encoding_test.rb @@ -1,9 +1,8 @@ require "abstract_unit" class ParameterEncodingController < ActionController::Base - parameter_encoding :test_bar, :bar, Encoding::ASCII_8BIT - parameter_encoding :test_baz, :baz, Encoding::ISO_8859_1 - parameter_encoding :test_baz_to_ascii, :baz, Encoding::ASCII_8BIT + skip_parameter_encoding :test_bar + skip_parameter_encoding :test_all_values_encoding def test_foo render body: params[:foo].encoding @@ -13,16 +12,8 @@ class ParameterEncodingController < ActionController::Base render body: params[:bar].encoding end - def test_baz - render body: params[:baz].encoding - end - - def test_no_change_to_baz - render body: params[:baz].encoding - end - - def test_baz_to_ascii - render body: params[:baz].encoding + def test_all_values_encoding + render body: ::JSON.dump(params.values.map(&:encoding).map(&:name)) end end @@ -36,32 +27,18 @@ class ParameterEncodingTest < ActionController::TestCase assert_equal "UTF-8", @response.body end - test "properly transcodes ASCII_8BIT parameters into declared encodings" do + test "properly encodes ASCII_8BIT parameters into binary" do post :test_bar, params: { "foo" => "foo", "bar" => "bar", "baz" => "baz" } assert_response :success assert_equal "ASCII-8BIT", @response.body end - test "properly transcodes ISO_8859_1 parameters into declared encodings" do - post :test_baz, params: { "foo" => "foo", "bar" => "bar", "baz" => "baz" } - - assert_response :success - assert_equal "ISO-8859-1", @response.body - end - - test "does not transcode parameters when not specified" do - post :test_no_change_to_baz, params: { "foo" => "foo", "bar" => "bar", "baz" => "baz" } + test "properly encodes all ASCII_8BIT parameters into binary" do + post :test_all_values_encoding, params: { "foo" => "foo", "bar" => "bar", "baz" => "baz" } assert_response :success - assert_equal "UTF-8", @response.body - end - - test "respects different encoding declarations for a param per action" do - post :test_baz_to_ascii, params: { "foo" => "foo", "bar" => "bar", "baz" => "baz" } - - assert_response :success - assert_equal "ASCII-8BIT", @response.body + assert_equal ["ASCII-8BIT"], JSON.parse(@response.body).uniq end test "does not raise an error when passed a param declared as ASCII-8BIT that contains invalid bytes" do diff --git a/actionpack/test/controller/parameters/parameters_permit_test.rb b/actionpack/test/controller/parameters/parameters_permit_test.rb index 92e134d313..b62a3d6d7b 100644 --- a/actionpack/test/controller/parameters/parameters_permit_test.rb +++ b/actionpack/test/controller/parameters/parameters_permit_test.rb @@ -141,7 +141,7 @@ class ParametersPermitTest < ActiveSupport::TestCase permitted = params.permit(:a, c: [], b: []) assert_equal 1, permitted[:a] assert_equal [1, 2, 3], permitted[:b] - assert_equal nil, permitted[:c] + assert_nil permitted[:c] end test "key to empty array: arrays of permitted scalars pass" do @@ -216,7 +216,7 @@ class ParametersPermitTest < ActiveSupport::TestCase test "fetch with a default value of a hash does not mutate the object" do params = ActionController::Parameters.new({}) params.fetch :foo, {} - assert_equal nil, params[:foo] + assert_nil params[:foo] end test "hashes in array values get wrapped" do @@ -254,8 +254,8 @@ class ParametersPermitTest < ActiveSupport::TestCase end test "fetch doesnt raise ParameterMissing exception if there is a default that is nil" do - assert_equal nil, @params.fetch(:foo, nil) - assert_equal nil, @params.fetch(:foo) { nil } + assert_nil @params.fetch(:foo, nil) + assert_nil @params.fetch(:foo) { nil } end test "KeyError in fetch block should not be covered up" do diff --git a/actionpack/test/controller/params_wrapper_test.rb b/actionpack/test/controller/params_wrapper_test.rb index 46a2ab8ccf..faa57c4559 100644 --- a/actionpack/test/controller/params_wrapper_test.rb +++ b/actionpack/test/controller/params_wrapper_test.rb @@ -50,7 +50,7 @@ class ParamsWrapperTest < ActionController::TestCase with_default_wrapper_options do @request.env["CONTENT_TYPE"] = "application/json" post :parse, params: { "username" => "sikachu" } - assert_equal @request.filtered_parameters, "controller" => "params_wrapper_test/users", "action" => "parse", "username" => "sikachu", "user" => { "username" => "sikachu" } + assert_equal({ "controller" => "params_wrapper_test/users", "action" => "parse", "username" => "sikachu", "user" => { "username" => "sikachu" } }, @request.filtered_parameters) end end diff --git a/actionpack/test/controller/redirect_test.rb b/actionpack/test/controller/redirect_test.rb index 0e61b92bcf..e4e968dfdb 100644 --- a/actionpack/test/controller/redirect_test.rb +++ b/actionpack/test/controller/redirect_test.rb @@ -123,7 +123,7 @@ class RedirectController < ActionController::Base def rescue_errors(e) raise e end - protected + private def dashbord_url(id, message) url_for action: "dashboard", params: { "id" => id, "message" => message } end diff --git a/actionpack/test/controller/request/test_request_test.rb b/actionpack/test/controller/request/test_request_test.rb index da6fa1388b..b67ae72c0a 100644 --- a/actionpack/test/controller/request/test_request_test.rb +++ b/actionpack/test/controller/request/test_request_test.rb @@ -8,7 +8,7 @@ class ActionController::TestRequestTest < ActionController::TestCase def test_mutating_session_options_does_not_affect_default_options @request.session_options[:myparam] = 123 - assert_equal nil, ActionController::TestSession::DEFAULT_OPTIONS[:myparam] + assert_nil ActionController::TestSession::DEFAULT_OPTIONS[:myparam] end def test_content_length_has_bytes_count_value @@ -17,8 +17,8 @@ class ActionController::TestRequestTest < ActionController::TestCase @request.set_header "CONTENT_TYPE", "application/json" @request.assign_parameters(@routes, "test", "create", non_ascii_parameters, "/test", [:data, :controller, :action]) - assert_equal(@request.get_header("CONTENT_LENGTH"), - StringIO.new(non_ascii_parameters.to_json).length.to_s) + assert_equal(StringIO.new(non_ascii_parameters.to_json).length.to_s, + @request.get_header("CONTENT_LENGTH")) end ActionDispatch::Session::AbstractStore::DEFAULT_OPTIONS.each_key do |option| diff --git a/actionpack/test/controller/request_forgery_protection_test.rb b/actionpack/test/controller/request_forgery_protection_test.rb index 90d5ab3c67..d645ddfdbe 100644 --- a/actionpack/test/controller/request_forgery_protection_test.rb +++ b/actionpack/test/controller/request_forgery_protection_test.rb @@ -92,7 +92,7 @@ class PrependProtectForgeryBaseController < ActionController::Base render inline: "OK" end - protected + private def add_called_callback(name) @called_callbacks ||= [] diff --git a/actionpack/test/controller/rescue_test.rb b/actionpack/test/controller/rescue_test.rb index a98e6479b7..9ae22c4554 100644 --- a/actionpack/test/controller/rescue_test.rb +++ b/actionpack/test/controller/rescue_test.rb @@ -149,7 +149,7 @@ class RescueController < ActionController::Base raise RangeError end - protected + private def deny_access head :forbidden end @@ -327,7 +327,7 @@ class RescueTest < ActionDispatch::IntegrationTest raise "b00m" end - protected + private def show_errors(exception) render plain: exception.message end diff --git a/actionpack/test/controller/resources_test.rb b/actionpack/test/controller/resources_test.rb index 0b3dc6c41f..fad34dacce 100644 --- a/actionpack/test/controller/resources_test.rb +++ b/actionpack/test/controller/resources_test.rb @@ -1089,7 +1089,7 @@ class ResourcesTest < ActionController::TestCase end end - protected + private def with_restful_routing(*args) options = args.extract_options! collection_methods = options.delete(:collection) diff --git a/actionpack/test/controller/send_file_test.rb b/actionpack/test/controller/send_file_test.rb index a28283f4d6..9e6b975fe2 100644 --- a/actionpack/test/controller/send_file_test.rb +++ b/actionpack/test/controller/send_file_test.rb @@ -241,10 +241,17 @@ class SendFileTest < ActionController::TestCase assert_equal "text/calendar; charset=utf-8", response.headers["Content-Type"] end + def test_send_file_charset_with_type_options_key_without_charset + @controller = SendFileWithActionControllerLive.new + @controller.options = { type: "image/png" } + response = process("file") + assert_equal "image/png", response.headers["Content-Type"] + end + def test_send_file_charset_with_content_type_options_key @controller = SendFileWithActionControllerLive.new @controller.options = { content_type: "text/calendar" } response = process("file") - assert_equal "text/calendar; charset=utf-8", response.headers["Content-Type"] + assert_equal "text/calendar", response.headers["Content-Type"] end end diff --git a/actionpack/test/controller/test_case_test.rb b/actionpack/test/controller/test_case_test.rb index 05aa4ff6ad..874f9c3c42 100644 --- a/actionpack/test/controller/test_case_test.rb +++ b/actionpack/test/controller/test_case_test.rb @@ -100,11 +100,11 @@ HTML end def test_xml_output - response.content_type = "application/xml" + response.content_type = params[:response_as] render plain: <<XML <?xml version="1.0" encoding="UTF-8"?> <root> - <area>area is an empty tag in HTML, raising an error if not in xml mode</area> + <area><p>area is an empty tag in HTML, so it won't contain this content</p></area> </root> XML end @@ -374,18 +374,18 @@ XML assert_equal "OK", @response.body end - def test_should_not_impose_childless_html_tags_in_xml - process :test_xml_output + def test_should_impose_childless_html_tags_in_html + process :test_xml_output, params: { response_as: "text/html" } - begin - $stderr = StringIO.new - assert_select "area" #This will cause a warning if content is processed as HTML - $stderr.rewind && err = $stderr.read - ensure - $stderr = STDERR - end + # <area> auto-closes, so the <p> becomes a sibling + assert_select "root > area + p" + end + + def test_should_not_impose_childless_html_tags_in_xml + process :test_xml_output, params: { response_as: "application/xml" } - assert err.empty?, err.inspect + # <area> is not special, so the <p> is its child + assert_select "root > area > p" end def test_assert_generates diff --git a/actionpack/test/controller/url_for_test.rb b/actionpack/test/controller/url_for_test.rb index 4b6f33c545..862dcf01c3 100644 --- a/actionpack/test/controller/url_for_test.rb +++ b/actionpack/test/controller/url_for_test.rb @@ -26,7 +26,7 @@ module AbstractController action: :index } }.url_helpers - self.default_url_options[:host] = "example.com" + default_url_options[:host] = "example.com" } path = klass.new.fun_path(controller: :articles, @@ -487,6 +487,27 @@ module AbstractController end end + def test_default_params_first_empty + with_routing do |set| + set.draw do + get "(:param1)/test(/:param2)" => "index#index", + defaults: { + param1: 1, + param2: 2 + }, + constraints: { + param1: /\d*/, + param2: /\d+/ + } + end + + kls = Class.new { include set.url_helpers } + kls.default_url_options[:host] = "www.basecamphq.com" + + assert_equal "http://www.basecamphq.com/test", kls.new.url_for(controller: "index", param1: "1") + end + end + private def extract_params(url) url.split("?", 2).last.split("&").sort diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb index af3036d448..73ad677419 100644 --- a/actionpack/test/dispatch/cookies_test.rb +++ b/actionpack/test/dispatch/cookies_test.rb @@ -272,6 +272,10 @@ class CookiesTest < ActionController::TestCase def noop head :ok end + + def encrypted_cookie + cookies.encrypted["foo"] + end end tests TestController @@ -391,6 +395,15 @@ class CookiesTest < ActionController::TestCase assert_equal false, cookies.deleted?("another") end + # Ensure all HTTP methods have their cookies updated + [:get, :post, :patch, :put, :delete, :head].each do |method| + define_method("test_deleting_cookie_#{method}") do + request.cookies[:user_name] = "Joe" + public_send method, :logout + assert_nil cookies[:user_name] + end + end + def test_deleted_cookie_predicate_with_mismatching_options cookies[:user_name] = "Joe" cookies.delete("user_name", path: "/path") @@ -940,8 +953,8 @@ class CookiesTest < ActionController::TestCase @request.headers["Cookie"] = "user_id=45" get :get_signed_cookie - assert_equal nil, @controller.send(:cookies).signed[:user_id] - assert_equal nil, @response.cookies["user_id"] + assert_nil @controller.send(:cookies).signed[:user_id] + assert_nil @response.cookies["user_id"] end def test_legacy_signed_cookie_is_treated_as_nil_by_encrypted_cookie_jar_if_tampered @@ -951,8 +964,8 @@ class CookiesTest < ActionController::TestCase @request.headers["Cookie"] = "foo=baz" get :get_encrypted_cookie - assert_equal nil, @controller.send(:cookies).encrypted[:foo] - assert_equal nil, @response.cookies["foo"] + assert_nil @controller.send(:cookies).encrypted[:foo] + assert_nil @response.cookies["foo"] end def test_cookie_with_all_domain_option @@ -1189,6 +1202,12 @@ class CookiesTest < ActionController::TestCase assert_equal "david", cookies[:user_name] end + def test_cookies_are_not_cleared + cookies.encrypted["foo"] = "bar" + get :noop + assert_equal "bar", @controller.encrypted_cookie + end + private def assert_cookie_header(expected) header = @response.headers["Set-Cookie"] diff --git a/actionpack/test/dispatch/request/multipart_params_parsing_test.rb b/actionpack/test/dispatch/request/multipart_params_parsing_test.rb index eb4bb14ed1..01c5ff1429 100644 --- a/actionpack/test/dispatch/request/multipart_params_parsing_test.rb +++ b/actionpack/test/dispatch/request/multipart_params_parsing_test.rb @@ -129,7 +129,7 @@ class MultipartParamsParsingTest < ActionDispatch::IntegrationTest params = parse_multipart("none") assert_equal %w(submit-name), params.keys.sort assert_equal "Larry", params["submit-name"] - assert_equal nil, params["files"] + assert_nil params["files"] end test "parses empty upload file" do diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb index 2f41851598..e11b93b4f0 100644 --- a/actionpack/test/dispatch/request_test.rb +++ b/actionpack/test/dispatch/request_test.rb @@ -18,7 +18,7 @@ class BaseRequestTest < ActiveSupport::TestCase ActionDispatch::Http::URL.url_for(options) end - protected + private def stub_request(env = {}) ip_spoofing_check = env.key?(:ip_spoofing_check) ? env.delete(:ip_spoofing_check) : true @trusted_proxies ||= nil @@ -94,13 +94,13 @@ class RequestIP < BaseRequestTest assert_equal "3.4.5.6", request.remote_ip request = stub_request "HTTP_X_FORWARDED_FOR" => "unknown,192.168.0.1" - assert_equal nil, request.remote_ip + assert_nil request.remote_ip request = stub_request "HTTP_X_FORWARDED_FOR" => "9.9.9.9, 3.4.5.6, 172.31.4.4, 10.0.0.1" assert_equal "3.4.5.6", request.remote_ip request = stub_request "HTTP_X_FORWARDED_FOR" => "not_ip_address" - assert_equal nil, request.remote_ip + assert_nil request.remote_ip end test "remote ip spoof detection" do @@ -154,7 +154,7 @@ class RequestIP < BaseRequestTest assert_equal "fe80:0000:0000:0000:0202:b3ff:fe1e:8329", request.remote_ip request = stub_request "HTTP_X_FORWARDED_FOR" => "unknown,::1" - assert_equal nil, request.remote_ip + assert_nil request.remote_ip request = stub_request "HTTP_X_FORWARDED_FOR" => "2001:0db8:85a3:0000:0000:8a2e:0370:7334, fe80:0000:0000:0000:0202:b3ff:fe1e:8329, ::1, fc00::, fc01::, fdff" assert_equal "fe80:0000:0000:0000:0202:b3ff:fe1e:8329", request.remote_ip @@ -163,7 +163,7 @@ class RequestIP < BaseRequestTest assert_equal "FE00::", request.remote_ip request = stub_request "HTTP_X_FORWARDED_FOR" => "not_ip_address" - assert_equal nil, request.remote_ip + assert_nil request.remote_ip end test "remote ip v6 spoof detection" do @@ -200,7 +200,7 @@ class RequestIP < BaseRequestTest assert_equal "3.4.5.6", request.remote_ip request = stub_request "HTTP_X_FORWARDED_FOR" => "67.205.106.73,unknown" - assert_equal nil, request.remote_ip + assert_nil request.remote_ip request = stub_request "HTTP_X_FORWARDED_FOR" => "9.9.9.9, 3.4.5.6, 10.0.0.1, 67.205.106.73" assert_equal "3.4.5.6", request.remote_ip @@ -222,7 +222,7 @@ class RequestIP < BaseRequestTest assert_equal "::1", request.remote_ip request = stub_request "HTTP_X_FORWARDED_FOR" => "unknown,fe80:0000:0000:0000:0202:b3ff:fe1e:8329" - assert_equal nil, request.remote_ip + assert_nil request.remote_ip request = stub_request "HTTP_X_FORWARDED_FOR" => "fe80:0000:0000:0000:0202:b3ff:fe1e:8329,2001:0db8:85a3:0000:0000:8a2e:0370:7334" assert_equal "2001:0db8:85a3:0000:0000:8a2e:0370:7334", request.remote_ip @@ -345,7 +345,7 @@ class RequestPort < BaseRequestTest test "optional port" do request = stub_request "HTTP_HOST" => "www.example.org:80" - assert_equal nil, request.optional_port + assert_nil request.optional_port request = stub_request "HTTP_HOST" => "www.example.org:8080" assert_equal 8080, request.optional_port @@ -537,7 +537,7 @@ class RequestCGI < BaseRequestTest assert_equal "Basic", request.auth_type assert_equal 0, request.content_length - assert_equal nil, request.content_mime_type + assert_nil request.content_mime_type assert_equal "CGI/1.1", request.gateway_interface assert_equal "*/*", request.accept assert_equal "UTF-8", request.accept_charset @@ -957,7 +957,7 @@ class RequestMimeType < BaseRequestTest end test "no content type" do - assert_equal nil, stub_request.content_mime_type + assert_nil stub_request.content_mime_type end test "content type is XML" do @@ -978,7 +978,7 @@ class RequestMimeType < BaseRequestTest "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest" ) - assert_equal nil, request.negotiate_mime([Mime[:xml], Mime[:json]]) + assert_nil request.negotiate_mime([Mime[:xml], Mime[:json]]) assert_equal Mime[:html], request.negotiate_mime([Mime[:xml], Mime[:html]]) assert_equal Mime[:html], request.negotiate_mime([Mime[:xml], Mime::ALL]) end @@ -1192,7 +1192,7 @@ class RequestEtag < BaseRequestTest test "doesn't match absent If-None-Match" do request = stub_request - assert_equal nil, request.if_none_match + assert_nil request.if_none_match assert_equal [], request.if_none_match_etags assert_not request.etag_matches?("foo") diff --git a/actionpack/test/dispatch/response_test.rb b/actionpack/test/dispatch/response_test.rb index 2df70704a1..7433c5ce0c 100644 --- a/actionpack/test/dispatch/response_test.rb +++ b/actionpack/test/dispatch/response_test.rb @@ -74,7 +74,7 @@ class ResponseTest < ActiveSupport::TestCase @response.body = "Hello, World!" # even though there's no explicitly set content-type, - assert_equal nil, @response.content_type + assert_nil @response.content_type # after the action reads back @response.body, assert_equal "Hello, World!", @response.body @@ -111,8 +111,8 @@ class ResponseTest < ActiveSupport::TestCase end def test_empty_content_type_returns_nil - @response.headers['Content-Type'] = "" - assert_equal nil, @response.content_type + @response.headers["Content-Type"] = "" + assert_nil @response.content_type end test "simple output" do diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 92d323f292..53758a4fbc 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -1603,7 +1603,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest get "account/login", to: redirect("/login") end - previous_host, self.host = self.host, "www.example.com:3000" + previous_host, self.host = host, "www.example.com:3000" get "/account/login" verify_redirect "http://www.example.com:3000/login" @@ -4189,7 +4189,7 @@ class TestConstraintsAccessingParameters < ActionDispatch::IntegrationTest test "parameters are reset between constraint checks" do get "/bar" - assert_equal nil, @request.params[:foo] + assert_nil @request.params[:foo] assert_equal "bar", @request.params[:bar] end end diff --git a/actionpack/test/dispatch/session/cache_store_test.rb b/actionpack/test/dispatch/session/cache_store_test.rb index a60629a7ee..859059063f 100644 --- a/actionpack/test/dispatch/session/cache_store_test.rb +++ b/actionpack/test/dispatch/session/cache_store_test.rb @@ -142,20 +142,20 @@ class CacheStoreTest < ActionDispatch::IntegrationTest get "/get_session_value" assert_response :success - assert_equal nil, headers["Set-Cookie"], "should not resend the cookie again if session_id cookie is already exists" + assert_nil headers["Set-Cookie"], "should not resend the cookie again if session_id cookie is already exists" end end def test_prevents_session_fixation with_test_route_set do - assert_equal nil, @cache.read("_session_id:0xhax") + assert_nil @cache.read("_session_id:0xhax") cookies["_session_id"] = "0xhax" get "/set_session_value" assert_response :success assert_not_equal "0xhax", cookies["_session_id"] - assert_equal nil, @cache.read("_session_id:0xhax") + assert_nil @cache.read("_session_id:0xhax") assert_equal({ "foo" => "bar" }, @cache.read("_session_id:#{cookies['_session_id']}")) end end diff --git a/actionpack/test/dispatch/session/cookie_store_test.rb b/actionpack/test/dispatch/session/cookie_store_test.rb index 013d289c6d..2a1053be16 100644 --- a/actionpack/test/dispatch/session/cookie_store_test.rb +++ b/actionpack/test/dispatch/session/cookie_store_test.rb @@ -108,7 +108,7 @@ class CookieStoreTest < ActionDispatch::IntegrationTest with_test_route_set(secure: true) do get "/set_session_value" assert_response :success - assert_equal nil, headers["Set-Cookie"] + assert_nil headers["Set-Cookie"] end end @@ -169,7 +169,7 @@ class CookieStoreTest < ActionDispatch::IntegrationTest with_test_route_set do get "/no_session_access" assert_response :success - assert_equal nil, headers["Set-Cookie"] + assert_nil headers["Set-Cookie"] end end @@ -179,7 +179,7 @@ class CookieStoreTest < ActionDispatch::IntegrationTest "fef868465920f415f2c0652d6910d3af288a0367" get "/no_session_access" assert_response :success - assert_equal nil, headers["Set-Cookie"] + assert_nil headers["Set-Cookie"] end end diff --git a/actionpack/test/dispatch/session/mem_cache_store_test.rb b/actionpack/test/dispatch/session/mem_cache_store_test.rb index c2d0719b4e..121e9ebef7 100644 --- a/actionpack/test/dispatch/session/mem_cache_store_test.rb +++ b/actionpack/test/dispatch/session/mem_cache_store_test.rb @@ -157,7 +157,7 @@ class MemCacheStoreTest < ActionDispatch::IntegrationTest get "/get_session_value" assert_response :success - assert_equal nil, headers["Set-Cookie"], "should not resend the cookie again if session_id cookie is already exists" + assert_nil headers["Set-Cookie"], "should not resend the cookie again if session_id cookie is already exists" end rescue Dalli::RingError => ex skip ex.message, ex.backtrace diff --git a/actionpack/test/dispatch/static_test.rb b/actionpack/test/dispatch/static_test.rb index 3facbf59c2..d8bc96e3e0 100644 --- a/actionpack/test/dispatch/static_test.rb +++ b/actionpack/test/dispatch/static_test.rb @@ -177,9 +177,9 @@ module StaticTests last_modified = File.mtime(File.join(@root, "#{file_name}.gz")) response = get(file_name, "HTTP_ACCEPT_ENCODING" => "gzip", "HTTP_IF_MODIFIED_SINCE" => last_modified.httpdate) assert_equal 304, response.status - assert_equal nil, response.headers["Content-Type"] - assert_equal nil, response.headers["Content-Encoding"] - assert_equal nil, response.headers["Vary"] + assert_nil response.headers["Content-Type"] + assert_nil response.headers["Content-Encoding"] + assert_nil response.headers["Vary"] end def test_serves_files_with_headers diff --git a/actionpack/test/dispatch/test_request_test.rb b/actionpack/test/dispatch/test_request_test.rb index b479af781d..85a6df4975 100644 --- a/actionpack/test/dispatch/test_request_test.rb +++ b/actionpack/test/dispatch/test_request_test.rb @@ -30,7 +30,7 @@ class TestRequestTest < ActiveSupport::TestCase req = ActionDispatch::TestRequest.create({}) assert_equal({}, req.cookies) - assert_equal nil, req.env["HTTP_COOKIE"] + assert_nil req.env["HTTP_COOKIE"] req.cookie_jar["user_name"] = "david" assert_cookies({ "user_name" => "david" }, req.cookie_jar) diff --git a/actionpack/test/journey/route_test.rb b/actionpack/test/journey/route_test.rb index d2a8163ffb..8fd73970b8 100644 --- a/actionpack/test/journey/route_test.rb +++ b/actionpack/test/journey/route_test.rb @@ -96,7 +96,7 @@ module ActionDispatch path = Path::Pattern.from_string "/:controller(/:action(/:id))(.:format)" generic = Route.build "name", nil, path, constraints, [], {} - knowledge = { id: 20, controller: "pages", action: "show" } + knowledge = { "id" => true, "controller" => true, "action" => true } routes = [specific, generic] diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md index 8da2e4ae1d..59afed1f98 100644 --- a/actionview/CHANGELOG.md +++ b/actionview/CHANGELOG.md @@ -1,3 +1,13 @@ +* Add `check_parameters` option to `current_page?` which makes it more strict. + + *Maksym Pugach* + +* Return correct object name in form helper method after `fields_for`. + + Fixes #26931. + + *Yuji Yaginuma* + * Use `ActionView::Resolver.caching?` (`config.action_view.cache_template_loading`) to enable template recompilation. diff --git a/actionview/MIT-LICENSE b/actionview/MIT-LICENSE index 8573eb1225..ac810e86d0 100644 --- a/actionview/MIT-LICENSE +++ b/actionview/MIT-LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2016 David Heinemeier Hansson +Copyright (c) 2004-2017 David Heinemeier Hansson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/actionview/RUNNING_UJS_TESTS.rdoc b/actionview/RUNNING_UJS_TESTS.rdoc index cbf6bdccc6..a575624a06 100644 --- a/actionview/RUNNING_UJS_TESTS.rdoc +++ b/actionview/RUNNING_UJS_TESTS.rdoc @@ -4,4 +4,4 @@ Ensure that you can build the project and run tests. Run rake ujs:server first, and then run the web tests by visiting http://localhost:4567 in your browser. -rake test:server +rake ujs:server diff --git a/actionview/Rakefile b/actionview/Rakefile index 48f17062ce..cba4684076 100644 --- a/actionview/Rakefile +++ b/actionview/Rakefile @@ -49,7 +49,7 @@ end namespace :ujs do desc "Starts the test server" task :server do - system 'bundle exec rackup test/ujs/config.ru -p 4567 -s puma' + system "bundle exec rackup test/ujs/config.ru -p 4567 -s puma" end end diff --git a/actionview/lib/action_view.rb b/actionview/lib/action_view.rb index ba6755be82..ca586f1d52 100644 --- a/actionview/lib/action_view.rb +++ b/actionview/lib/action_view.rb @@ -1,5 +1,5 @@ #-- -# Copyright (c) 2004-2016 David Heinemeier Hansson +# Copyright (c) 2004-2017 David Heinemeier Hansson # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the diff --git a/actionview/lib/action_view/helpers/asset_tag_helper.rb b/actionview/lib/action_view/helpers/asset_tag_helper.rb index 4e4f4823e6..72a094c629 100644 --- a/actionview/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionview/lib/action_view/helpers/asset_tag_helper.rb @@ -35,18 +35,37 @@ module ActionView # When the Asset Pipeline is enabled, you can pass the name of your manifest as # source, and include other JavaScript or CoffeeScript files inside the manifest. # + # ==== Options + # + # When the last parameter is a hash you can add HTML attributes using that + # parameter. The following options are supported: + # + # * <tt>:extname</tt> - Append a extention to the generated url unless the extension + # already exists. This only applies for relative urls. + # * <tt>:protocol</tt> - Sets the protocol of the generated url, this option only + # applies when a relative url and +host+ options are provided. + # * <tt>:host</tt> - When a relative url is provided the host is added to the + # that path. + # * <tt>:skip_pipeline</tt> - This option is used to bypass the asset pipeline + # when it is set to true. + # + # ==== Examples + # # javascript_include_tag "xmlhr" - # # => <script src="/assets/xmlhr.js?1284139606"></script> + # # => <script src="/assets/xmlhr.debug-1284139606.js"></script> + # + # javascript_include_tag "xmlhr", host: "localhost", protocol: "https" + # # => <script src="https://localhost/assets/xmlhr.debug-1284139606.js"></script> # # javascript_include_tag "template.jst", extname: false - # # => <script src="/assets/template.jst?1284139606"></script> + # # => <script src="/assets/template.debug-1284139606.jst"></script> # # javascript_include_tag "xmlhr.js" - # # => <script src="/assets/xmlhr.js?1284139606"></script> + # # => <script src="/assets/xmlhr.debug-1284139606.js"></script> # # javascript_include_tag "common.javascript", "/elsewhere/cools" - # # => <script src="/assets/common.javascript?1284139606"></script> - # # <script src="/elsewhere/cools.js?1423139606"></script> + # # => <script src="/assets/common.javascript.debug-1284139606.js"></script> + # # <script src="/elsewhere/cools.debug-1284139606.js"></script> # # javascript_include_tag "http://www.example.com/xmlhr" # # => <script src="http://www.example.com/xmlhr"></script> diff --git a/actionview/lib/action_view/helpers/cache_helper.rb b/actionview/lib/action_view/helpers/cache_helper.rb index bf1c8ceaed..7fdf0fd0e1 100644 --- a/actionview/lib/action_view/helpers/cache_helper.rb +++ b/actionview/lib/action_view/helpers/cache_helper.rb @@ -215,7 +215,7 @@ module ActionView private - def fragment_name_with_digest(name, virtual_path) #:nodoc: + def fragment_name_with_digest(name, virtual_path) virtual_path ||= @virtual_path if virtual_path name = controller.url_for(name).split("://").last if name.is_a?(Hash) @@ -226,7 +226,7 @@ module ActionView end end - def fragment_for(name = {}, options = nil, &block) #:nodoc: + def fragment_for(name = {}, options = nil, &block) if content = read_fragment_for(name, options) @cache_hit = true content @@ -236,11 +236,11 @@ module ActionView end end - def read_fragment_for(name, options) #:nodoc: + def read_fragment_for(name, options) controller.read_fragment(name, options) end - def write_fragment_for(name, options) #:nodoc: + def write_fragment_for(name, options) # VIEW TODO: Make #capture usable outside of ERB # This dance is needed because Builder can't use capture pos = output_buffer.length diff --git a/actionview/lib/action_view/helpers/form_helper.rb b/actionview/lib/action_view/helpers/form_helper.rb index 6c632b8e75..26a625e4fe 100644 --- a/actionview/lib/action_view/helpers/form_helper.rb +++ b/actionview/lib/action_view/helpers/form_helper.rb @@ -513,6 +513,17 @@ module ActionView # <input type="text" name="post[title]" value="<the title of the post>"> # </form> # + # # Though the fields don't have to correspond to model attributes: + # <%= form_with model: Cat.new do |form| %> + # <%= form.text_field :cats_dont_have_gills %> + # <%= form.text_field :but_in_forms_they_can %> + # <% end %> + # # => + # <form action="/cats" method="post" data-remote="true"> + # <input type="text" name="cat[cats_dont_have_gills]"> + # <input type="text" name="cat[but_in_forms_they_can]"> + # </form> + # # The parameters in the forms are accessible in controllers according to # their name nesting. So inputs named +title+ and <tt>post[title]</tt> are # accessible as <tt>params[:title]</tt> and <tt>params[:post][:title]</tt> @@ -521,7 +532,7 @@ module ActionView # By default +form_with+ attaches the <tt>data-remote</tt> attribute # submitting the form via an XMLHTTPRequest in the background if an # Unobtrusive JavaScript driver, like rails-ujs, is used. See the - # <tt>:remote</tt> option for more. + # <tt>:local</tt> option for more. # # For ease of comparison the examples above left out the submit button, # as well as the auto generated hidden fields that enable UTF-8 support @@ -700,6 +711,7 @@ module ActionView # form_with(**options.merge(builder: LabellingFormBuilder), &block) # end def form_with(model: nil, scope: nil, url: nil, format: nil, **options) + options[:allow_method_names_outside_object] = true options[:skip_default_ids] = true if model @@ -976,14 +988,14 @@ module ActionView # <%= fields :comment do |fields| %> # <%= fields.text_field :body %> # <% end %> - # # => <input type="text" name="comment[body] id="comment_body"> + # # => <input type="text" name="comment[body]> # # # Using a model infers the scope and assigns field values: # <%= fields model: Comment.new(body: "full bodied") do |fields| %< # <%= fields.text_field :body %> # <% end %> # # => - # <input type="text" name="comment[body] id="comment_body" value="full bodied"> + # <input type="text" name="comment[body] value="full bodied"> # # # Using +fields+ with +form_with+: # <%= form_with model: @post do |form| %> @@ -1025,6 +1037,7 @@ module ActionView # to work with an object as a base, like # FormOptionHelper#collection_select and DateHelper#datetime_select. def fields(scope = nil, model: nil, **options, &block) + options[:allow_method_names_outside_object] = true options[:skip_default_ids] = true if model @@ -1492,7 +1505,7 @@ module ActionView private def html_options_for_form_with(url_for_options = nil, model = nil, html: {}, local: false, skip_enforcing_utf8: false, **options) - html_options = options.except(:index, :include_id, :skip_default_ids, :builder).merge(html) + html_options = options.slice(:id, :class, :multipart, :method, :data).merge(html) html_options[:method] ||= :patch if model.respond_to?(:persisted?) && model.persisted? html_options[:enforce_utf8] = !skip_enforcing_utf8 @@ -1626,7 +1639,7 @@ module ActionView def initialize(object_name, object, template, options) @nested_child_index = {} @object_name, @object, @template, @options = object_name, object, template, options - @default_options = @options ? @options.slice(:index, :namespace, :skip_default_ids) : {} + @default_options = @options ? @options.slice(:index, :namespace, :skip_default_ids, :allow_method_names_outside_object) : {} convert_to_legacy_options(@options) @@ -1911,10 +1924,11 @@ module ActionView record_name = model_name_from_record_or_class(record_object).param_key end + object_name = @object_name index = if options.has_key?(:index) options[:index] elsif defined?(@auto_index) - self.object_name = @object_name.to_s.sub(/\[\]$/, "") + object_name = object_name.to_s.sub(/\[\]$/, "") @auto_index end @@ -1933,6 +1947,7 @@ module ActionView # See the docs for the <tt>ActionView::FormHelper.fields</tt> helper method. def fields(scope = nil, model: nil, **options, &block) + options[:allow_method_names_outside_object] = true options[:skip_default_ids] = true convert_to_legacy_options(options) diff --git a/actionview/lib/action_view/helpers/form_tag_helper.rb b/actionview/lib/action_view/helpers/form_tag_helper.rb index ffc52569f2..ffc64e7118 100644 --- a/actionview/lib/action_view/helpers/form_tag_helper.rb +++ b/actionview/lib/action_view/helpers/form_tag_helper.rb @@ -442,21 +442,9 @@ module ActionView # # => <input name='commit' type='submit' value='Save' data-disable-with="Save" data-confirm="Are you sure?" /> # def submit_tag(value = "Save changes", options = {}) - options = options.stringify_keys + options = options.deep_stringify_keys tag_options = { "type" => "submit", "name" => "commit", "value" => value }.update(options) - - if ActionView::Base.automatically_disable_submit_tag - unless tag_options["data-disable-with"] == false || (tag_options["data"] && tag_options["data"][:disable_with] == false) - disable_with_text = tag_options["data-disable-with"] - disable_with_text ||= tag_options["data"][:disable_with] if tag_options["data"] - disable_with_text ||= value.to_s.clone - tag_options.deep_merge!("data" => { "disable_with" => disable_with_text }) - else - tag_options["data"].delete(:disable_with) if tag_options["data"] - end - tag_options.delete("data-disable-with") - end - + set_default_disable_with value, tag_options tag :input, tag_options end @@ -898,6 +886,22 @@ module ActionView def sanitize_to_id(name) name.to_s.delete("]").tr("^-a-zA-Z0-9:.", "_") end + + def set_default_disable_with(value, tag_options) + return unless ActionView::Base.automatically_disable_submit_tag + data = tag_options["data"] + + unless tag_options["data-disable-with"] == false || (data && data["disable_with"] == false) + disable_with_text = tag_options["data-disable-with"] + disable_with_text ||= data["disable_with"] if data + disable_with_text ||= value.to_s.clone + tag_options.deep_merge!("data" => { "disable_with" => disable_with_text }) + else + data.delete("disable_with") if data + end + + tag_options.delete("data-disable-with") + end end end end diff --git a/actionview/lib/action_view/helpers/number_helper.rb b/actionview/lib/action_view/helpers/number_helper.rb index 75b898c3e9..9e80f0b2ee 100644 --- a/actionview/lib/action_view/helpers/number_helper.rb +++ b/actionview/lib/action_view/helpers/number_helper.rb @@ -171,6 +171,9 @@ module ActionView # to ","). # * <tt>:separator</tt> - Sets the separator between the # fractional and integer digits (defaults to "."). + # * <tt>:delimiter_pattern</tt> - Sets a custom regular expression used for + # deriving the placement of delimiter. Helpful when using currency formats + # like INR. # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when # the argument is invalid. # @@ -187,6 +190,9 @@ module ActionView # number_with_delimiter(98765432.98, delimiter: " ", separator: ",") # # => 98 765 432,98 # + # number_with_delimiter("123456.78", + # delimiter_pattern: /(\d+?)(?=(\d\d)+(\d)(?!\d))/) # => "1,23,456.78" + # # number_with_delimiter("112a", raise: true) # => raise InvalidNumberError def number_with_delimiter(number, options = {}) delegate_number_helper_method(:number_to_delimited, number, options) diff --git a/actionview/lib/action_view/helpers/tags/base.rb b/actionview/lib/action_view/helpers/tags/base.rb index b8c446cbed..0895533a60 100644 --- a/actionview/lib/action_view/helpers/tags/base.rb +++ b/actionview/lib/action_view/helpers/tags/base.rb @@ -14,8 +14,16 @@ module ActionView @object_name.sub!(/\[\]$/, "") || @object_name.sub!(/\[\]\]$/, "]") @object = retrieve_object(options.delete(:object)) @skip_default_ids = options.delete(:skip_default_ids) + @allow_method_names_outside_object = options.delete(:allow_method_names_outside_object) @options = options - @auto_index = Regexp.last_match ? retrieve_autoindex(Regexp.last_match.pre_match) : nil + + if Regexp.last_match + @generate_indexed_names = true + @auto_index = retrieve_autoindex(Regexp.last_match.pre_match) + else + @generate_indexed_names = false + @auto_index = nil + end end # This is what child classes implement. @@ -26,7 +34,11 @@ module ActionView private def value(object) - object.public_send @method_name if object + if @allow_method_names_outside_object + object.public_send @method_name if object && object.respond_to?(@method_name) + else + object.public_send @method_name if object + end end def value_before_type_cast(object) @@ -93,7 +105,10 @@ module ActionView def tag_name(multiple = false, index = nil) # a little duplication to construct less strings - if index + case + when @object_name.empty? + "#{sanitized_method_name}#{"[]" if multiple}" + when index "#{@object_name}[#{index}][#{sanitized_method_name}]#{"[]" if multiple}" else "#{@object_name}[#{sanitized_method_name}]#{"[]" if multiple}" @@ -102,7 +117,10 @@ module ActionView def tag_id(index = nil) # a little duplication to construct less strings - if index + case + when @object_name.empty? + sanitized_method_name.dup + when index "#{sanitized_object_name}_#{index}_#{sanitized_method_name}" else "#{sanitized_object_name}_#{sanitized_method_name}" @@ -156,7 +174,11 @@ module ActionView end def name_and_id_index(options) - options.key?("index") ? options.delete("index") || "" : @auto_index + if options.key?("index") + options.delete("index") || "" + elsif @generate_indexed_names + @auto_index || "" + end end def skip_default_ids? diff --git a/actionview/lib/action_view/helpers/tags/collection_check_boxes.rb b/actionview/lib/action_view/helpers/tags/collection_check_boxes.rb index 0359d4e65d..7252d4f2d9 100644 --- a/actionview/lib/action_view/helpers/tags/collection_check_boxes.rb +++ b/actionview/lib/action_view/helpers/tags/collection_check_boxes.rb @@ -24,7 +24,7 @@ module ActionView builder.check_box + builder.label end - def hidden_field_name #:nodoc: + def hidden_field_name "#{super}[]" end end diff --git a/actionview/lib/action_view/helpers/tags/collection_helpers.rb b/actionview/lib/action_view/helpers/tags/collection_helpers.rb index c8be392865..75d237eb35 100644 --- a/actionview/lib/action_view/helpers/tags/collection_helpers.rb +++ b/actionview/lib/action_view/helpers/tags/collection_helpers.rb @@ -43,7 +43,7 @@ module ActionView # Generate default options for collection helpers, such as :checked and # :disabled. - def default_html_options_for_collection(item, value) #:nodoc: + def default_html_options_for_collection(item, value) html_options = @html_options.dup [:checked, :selected, :disabled, :readonly].each do |option| @@ -67,11 +67,11 @@ module ActionView html_options end - def sanitize_attribute_name(value) #:nodoc: + def sanitize_attribute_name(value) "#{sanitized_method_name}_#{sanitized_value(value)}" end - def render_collection #:nodoc: + def render_collection @collection.map do |item| value = value_for_collection(item, @value_method) text = value_for_collection(item, @text_method) @@ -82,7 +82,7 @@ module ActionView end.join.html_safe end - def render_collection_for(builder_class, &block) #:nodoc: + def render_collection_for(builder_class, &block) options = @options.stringify_keys rendered_collection = render_collection do |item, value, text, default_html_options| builder = instantiate_builder(builder_class, item, value, text, default_html_options) @@ -103,12 +103,12 @@ module ActionView end end - def hidden_field #:nodoc: + def hidden_field hidden_name = @html_options[:name] || hidden_field_name @template_object.hidden_field_tag(hidden_name, "", id: nil) end - def hidden_field_name #:nodoc: + def hidden_field_name "#{tag_name(false, @options[:index])}" end end diff --git a/actionview/lib/action_view/helpers/tags/date_select.rb b/actionview/lib/action_view/helpers/tags/date_select.rb index 006605885a..638c134deb 100644 --- a/actionview/lib/action_view/helpers/tags/date_select.rb +++ b/actionview/lib/action_view/helpers/tags/date_select.rb @@ -16,7 +16,7 @@ module ActionView class << self def select_type - @select_type ||= self.name.split("::").last.sub("Select", "").downcase + @select_type ||= name.split("::").last.sub("Select", "").downcase end end diff --git a/actionview/lib/action_view/helpers/tags/text_field.rb b/actionview/lib/action_view/helpers/tags/text_field.rb index 4306c3543d..613cade7b3 100644 --- a/actionview/lib/action_view/helpers/tags/text_field.rb +++ b/actionview/lib/action_view/helpers/tags/text_field.rb @@ -17,7 +17,7 @@ module ActionView class << self def field_type - @field_type ||= self.name.split("::").last.sub("Field", "").downcase + @field_type ||= name.split("::").last.sub("Field", "").downcase end end diff --git a/actionview/lib/action_view/helpers/tags/translator.rb b/actionview/lib/action_view/helpers/tags/translator.rb index 62b1df81c6..ced835eaa8 100644 --- a/actionview/lib/action_view/helpers/tags/translator.rb +++ b/actionview/lib/action_view/helpers/tags/translator.rb @@ -14,6 +14,8 @@ module ActionView translated_attribute || human_attribute_name end + # TODO Change this to private once we've dropped Ruby 2.2 support. + # Workaround for Ruby 2.2 "private attribute?" warning. protected attr_reader :object_name, :method_and_value, :scope, :model diff --git a/actionview/lib/action_view/helpers/url_helper.rb b/actionview/lib/action_view/helpers/url_helper.rb index 22cc4b2920..1f753bccd6 100644 --- a/actionview/lib/action_view/helpers/url_helper.rb +++ b/actionview/lib/action_view/helpers/url_helper.rb @@ -517,6 +517,9 @@ module ActionView # current_page?('http://www.example.com/shop/checkout') # # => true # + # current_page?('http://www.example.com/shop/checkout', check_parameters: true) + # # => false + # # current_page?('/shop/checkout') # # => true # @@ -530,7 +533,7 @@ module ActionView # # We can also pass in the symbol arguments instead of strings. # - def current_page?(options) + def current_page?(options, check_parameters: false) unless request raise "You cannot use helpers that need to determine the current " \ "page unless your view context provides a Request object " \ @@ -539,12 +542,14 @@ module ActionView return false unless request.get? || request.head? + check_parameters ||= !options.is_a?(String) && options.try(:delete, :check_parameters) url_string = URI.parser.unescape(url_for(options)).force_encoding(Encoding::BINARY) # We ignore any extra parameters in the request_uri if the # submitted url doesn't have any either. This lets the function # work with things like ?order=asc - request_uri = url_string.index("?") ? request.fullpath : request.path + # the behaviour can be disabled with check_parameters: true + request_uri = url_string.index("?") || check_parameters ? request.fullpath : request.path request_uri = URI.parser.unescape(request_uri).force_encoding(Encoding::BINARY) url_string.chomp!("/") if url_string.start_with?("/") && url_string != "/" @@ -614,7 +619,7 @@ module ActionView # # to_form_params({ name: 'Denmark' }, 'country') # # => [{name: 'country[name]', value: 'Denmark'}] - def to_form_params(attribute, namespace = nil) # :nodoc: + def to_form_params(attribute, namespace = nil) attribute = if attribute.respond_to?(:permitted?) unless attribute.permitted? raise ArgumentError, "Attempting to generate a buttom from non-sanitized request parameters!" \ diff --git a/actionview/lib/action_view/layouts.rb b/actionview/lib/action_view/layouts.rb index 7499e3b951..81feb90486 100644 --- a/actionview/lib/action_view/layouts.rb +++ b/actionview/lib/action_view/layouts.rb @@ -319,7 +319,7 @@ module ActionView name_clause end - self.class_eval <<-RUBY, __FILE__, __LINE__ + 1 + class_eval <<-RUBY, __FILE__, __LINE__ + 1 def _layout(formats) if _conditional_layout? #{layout_definition} @@ -338,7 +338,7 @@ module ActionView # # ==== Returns # * <tt>String</tt> - A template name - def _implied_layout_name # :nodoc: + def _implied_layout_name controller_path end end diff --git a/actionview/lib/action_view/log_subscriber.rb b/actionview/lib/action_view/log_subscriber.rb index c9f308c2a2..d03e1a51b8 100644 --- a/actionview/lib/action_view/log_subscriber.rb +++ b/actionview/lib/action_view/log_subscriber.rb @@ -51,20 +51,20 @@ module ActionView ActionView::Base.logger end - protected + private EMPTY = "" - def from_rails_root(string) + def from_rails_root(string) # :doc: string = string.sub(rails_root, EMPTY) string.sub!(VIEWS_PATTERN, EMPTY) string end - def rails_root + def rails_root # :doc: @root ||= "#{Rails.root}/" end - def render_count(payload) + def render_count(payload) # :doc: if payload[:cache_hits] "[#{payload[:cache_hits]} / #{payload[:count]} cache hits]" else @@ -72,7 +72,7 @@ module ActionView end end - def cache_message(payload) + def cache_message(payload) # :doc: if payload[:cache_hit] "[cache hit]" else @@ -80,8 +80,6 @@ module ActionView end end - private - def log_rendering_start(payload) info do message = " Rendering #{from_rails_root(payload[:identifier])}" diff --git a/actionview/lib/action_view/lookup_context.rb b/actionview/lib/action_view/lookup_context.rb index 50faf1b8dd..f385a7cd04 100644 --- a/actionview/lib/action_view/lookup_context.rb +++ b/actionview/lib/action_view/lookup_context.rb @@ -93,9 +93,9 @@ module ActionView @cache = old_value end - protected + private - def _set_detail(key, value) + def _set_detail(key, value) # :doc: @details = @details.dup if @details_key @details_key = nil @details[key] = value @@ -149,16 +149,16 @@ module ActionView added_resolvers.times { view_paths.pop } end - protected + private - def args_for_lookup(name, prefixes, partial, keys, details_options) #:nodoc: + def args_for_lookup(name, prefixes, partial, keys, details_options) name, prefixes = normalize_name(name, prefixes) details, details_key = detail_args_for(details_options) [name, prefixes, partial || false, details, details_key, keys] end # Compute details hash and key according to user options (e.g. passed from #render). - def detail_args_for(options) + def detail_args_for(options) # :doc: return @details, details_key if options.empty? # most common path. user_details = @details.merge(options) @@ -171,13 +171,13 @@ module ActionView [user_details, details_key] end - def args_for_any(name, prefixes, partial) # :nodoc: + def args_for_any(name, prefixes, partial) name, prefixes = normalize_name(name, prefixes) details, details_key = detail_args_for_any [name, prefixes, partial || false, details, details_key] end - def detail_args_for_any # :nodoc: + def detail_args_for_any @detail_args_for_any ||= begin details = {} @@ -200,7 +200,7 @@ module ActionView # Support legacy foo.erb names even though we now ignore .erb # as well as incorrectly putting part of the path in the template # name instead of the prefix. - def normalize_name(name, prefixes) #:nodoc: + def normalize_name(name, prefixes) prefixes = prefixes.presence parts = name.to_s.split("/".freeze) parts.shift if parts.first.empty? diff --git a/actionview/lib/action_view/record_identifier.rb b/actionview/lib/action_view/record_identifier.rb index b39acfa0b5..48bea315a9 100644 --- a/actionview/lib/action_view/record_identifier.rb +++ b/actionview/lib/action_view/record_identifier.rb @@ -92,7 +92,7 @@ module ActionView end end - protected + private # Returns a string representation of the key attribute(s) that is suitable for use in an HTML DOM id. # This can be overwritten to customize the default generated string representation if desired. @@ -102,7 +102,7 @@ module ActionView # overwritten version of the method. By default, this implementation passes the key string through a # method that replaces all characters that are invalid inside DOM ids, with valid ones. You need to # make sure yourself that your dom ids are valid, in case you overwrite this method. - def record_key_for_dom_id(record) + def record_key_for_dom_id(record) # :doc: key = convert_to_model(record).to_key key ? key.join(JOIN) : key end diff --git a/actionview/lib/action_view/renderer/abstract_renderer.rb b/actionview/lib/action_view/renderer/abstract_renderer.rb index 3c85be49cd..0b315eb569 100644 --- a/actionview/lib/action_view/renderer/abstract_renderer.rb +++ b/actionview/lib/action_view/renderer/abstract_renderer.rb @@ -25,9 +25,9 @@ module ActionView raise NotImplementedError end - protected + private - def extract_details(options) + def extract_details(options) # :doc: @lookup_context.registered_details.each_with_object({}) do |key, details| value = options[key] @@ -35,7 +35,7 @@ module ActionView end end - def instrument(name, **options) + def instrument(name, **options) # :doc: options[:identifier] ||= (@template && @template.identifier) || @path ActiveSupport::Notifications.instrument("render_#{name}.action_view", options) do |payload| @@ -43,7 +43,7 @@ module ActionView end end - def prepend_formats(formats) + def prepend_formats(formats) # :doc: formats = Array(formats) return if formats.empty? || @lookup_context.html_fallback_for_js diff --git a/actionview/lib/action_view/renderer/streaming_template_renderer.rb b/actionview/lib/action_view/renderer/streaming_template_renderer.rb index 2434250b2d..7ede034492 100644 --- a/actionview/lib/action_view/renderer/streaming_template_renderer.rb +++ b/actionview/lib/action_view/renderer/streaming_template_renderer.rb @@ -29,7 +29,7 @@ module ActionView # This is the same logging logic as in ShowExceptions middleware. # TODO Once "exceptron" is in, refactor this piece to simply re-use exceptron. - def log_error(exception) #:nodoc: + def log_error(exception) logger = ActionView::Base.logger return unless logger diff --git a/actionview/lib/action_view/renderer/template_renderer.rb b/actionview/lib/action_view/renderer/template_renderer.rb index f40bf8f6e2..54317199de 100644 --- a/actionview/lib/action_view/renderer/template_renderer.rb +++ b/actionview/lib/action_view/renderer/template_renderer.rb @@ -44,7 +44,7 @@ module ActionView # Renders the given template. A string representing the layout can be # supplied as well. - def render_template(template, layout_name = nil, locals = nil) #:nodoc: + def render_template(template, layout_name = nil, locals = nil) view, locals = @view, locals || {} render_with_layout(layout_name, locals) do |layout| @@ -54,7 +54,7 @@ module ActionView end end - def render_with_layout(path, locals) #:nodoc: + def render_with_layout(path, locals) layout = path && find_layout(path, locals.keys, [formats.first]) content = yield(layout) diff --git a/actionview/lib/action_view/rendering.rb b/actionview/lib/action_view/rendering.rb index b70e7239fc..0e72316eb7 100644 --- a/actionview/lib/action_view/rendering.rb +++ b/actionview/lib/action_view/rendering.rb @@ -91,7 +91,7 @@ module ActionView # Find and render a template based on the options given. # :api: private - def _render_template(options) #:nodoc: + def _render_template(options) variant = options.delete(:variant) assigns = options.delete(:assigns) context = view_context @@ -104,7 +104,7 @@ module ActionView end # Assign the rendered format to look up context. - def _process_format(format) #:nodoc: + def _process_format(format) super lookup_context.formats = [format.to_sym] lookup_context.rendered_format = lookup_context.formats.first diff --git a/actionview/lib/action_view/routing_url_for.rb b/actionview/lib/action_view/routing_url_for.rb index 669cffab1a..687ba7c1b4 100644 --- a/actionview/lib/action_view/routing_url_for.rb +++ b/actionview/lib/action_view/routing_url_for.rb @@ -122,18 +122,15 @@ module ActionView controller.url_options end - def _routes_context #:nodoc: - controller - end - protected :_routes_context - - def optimize_routes_generation? #:nodoc: - controller.respond_to?(:optimize_routes_generation?, true) ? - controller.optimize_routes_generation? : super - end - protected :optimize_routes_generation? - private + def _routes_context + controller + end + + def optimize_routes_generation? + controller.respond_to?(:optimize_routes_generation?, true) ? + controller.optimize_routes_generation? : super + end def _generate_paths_by_default true diff --git a/actionview/lib/action_view/template.rb b/actionview/lib/action_view/template.rb index 0afdcd1def..4b793c3b16 100644 --- a/actionview/lib/action_view/template.rb +++ b/actionview/lib/action_view/template.rb @@ -140,7 +140,7 @@ module ActionView end # Returns whether the underlying handler supports streaming. If so, - # a streaming buffer *may* be passed when it start rendering. + # a streaming buffer *may* be passed when it starts rendering. def supports_streaming? handler.respond_to?(:supports_streaming?) && handler.supports_streaming? end @@ -231,11 +231,11 @@ module ActionView end end - protected + private # Compile a template. This method ensures a template is compiled # just once and removes the source after it is compiled. - def compile!(view) #:nodoc: + def compile!(view) return if @compiled # Templates can be used concurrently in threaded environments @@ -276,9 +276,8 @@ module ActionView # encode the source into <tt>Encoding.default_internal</tt>. # In general, this means that templates will be UTF-8 inside of Rails, # regardless of the original source encoding. - def compile(mod) #:nodoc: + def compile(mod) encode! - method_name = self.method_name code = @handler.call(self) # Make sure that the resulting String to be eval'd is in the @@ -309,7 +308,7 @@ module ActionView ObjectSpace.define_finalizer(self, Finalizer[method_name, mod]) end - def handle_render_error(view, e) #:nodoc: + def handle_render_error(view, e) if e.is_a?(Template::Error) e.sub_template_of(self) raise e @@ -323,7 +322,7 @@ module ActionView end end - def locals_code #:nodoc: + def locals_code # Only locals with valid variable names get set directly. Others will # still be available in local_assigns. locals = @locals - Module::RUBY_RESERVED_KEYWORDS @@ -333,7 +332,7 @@ module ActionView locals.each_with_object("") { |key, code| code << "#{key} = #{key} = local_assigns[:#{key}];" } end - def method_name #:nodoc: + def method_name @method_name ||= begin m = "_#{identifier_method_name}__#{@identifier.hash}_#{__id__}" m.tr!("-".freeze, "_".freeze) @@ -341,16 +340,14 @@ module ActionView end end - def identifier_method_name #:nodoc: + def identifier_method_name inspect.tr("^a-z_".freeze, "_".freeze) end - def instrument(action, &block) + def instrument(action, &block) # :doc: ActiveSupport::Notifications.instrument("#{action}.action_view".freeze, instrument_payload, &block) end - private - def instrument_render_template(&block) ActiveSupport::Notifications.instrument("!render_template.action_view".freeze, instrument_payload, &block) end diff --git a/actionview/lib/action_view/template/handlers/builder.rb b/actionview/lib/action_view/template/handlers/builder.rb index e08a5b5db8..494b543152 100644 --- a/actionview/lib/action_view/template/handlers/builder.rb +++ b/actionview/lib/action_view/template/handlers/builder.rb @@ -13,9 +13,9 @@ module ActionView ";xml.target!;" end - protected + private - def require_engine + def require_engine # :doc: @required ||= begin require "builder" true diff --git a/actionview/lib/action_view/template/resolver.rb b/actionview/lib/action_view/template/resolver.rb index ed93ebc027..9da13663d7 100644 --- a/actionview/lib/action_view/template/resolver.rb +++ b/actionview/lib/action_view/template/resolver.rb @@ -177,7 +177,7 @@ module ActionView # always check the cache before hitting the resolver. Otherwise, # it always hits the resolver but if the key is present, check if the # resolver is fresher before returning it. - def cached(key, path_info, details, locals) #:nodoc: + def cached(key, path_info, details, locals) name, prefix, partial = path_info locals = locals.map(&:to_s).sort! @@ -191,7 +191,7 @@ module ActionView end # Ensures all the resolver information is set in the template. - def decorate(templates, path_info, details, locals) #:nodoc: + def decorate(templates, path_info, details, locals) cached = nil templates.each do |t| t.locals = locals diff --git a/actionview/lib/action_view/test_case.rb b/actionview/lib/action_view/test_case.rb index 5fb7bb54b5..2b981caa65 100644 --- a/actionview/lib/action_view/test_case.rb +++ b/actionview/lib/action_view/test_case.rb @@ -206,8 +206,8 @@ module ActionView view = @controller.view_context view.singleton_class.include(_helpers) view.extend(Locals) - view.rendered_views = self.rendered_views - view.output_buffer = self.output_buffer + view.rendered_views = rendered_views + view.output_buffer = output_buffer view end end diff --git a/actionview/lib/action_view/view_paths.rb b/actionview/lib/action_view/view_paths.rb index a9638d1e6d..f0fe6831fa 100644 --- a/actionview/lib/action_view/view_paths.rb +++ b/actionview/lib/action_view/view_paths.rb @@ -5,7 +5,7 @@ module ActionView included do class_attribute :_view_paths self._view_paths = ActionView::PathSet.new - self._view_paths.freeze + _view_paths.freeze end delegate :template_exists?, :any_templates?, :view_paths, :formats, :formats=, diff --git a/actionview/test/abstract_unit.rb b/actionview/test/abstract_unit.rb index 88c7189d22..fbdaa07fb0 100644 --- a/actionview/test/abstract_unit.rb +++ b/actionview/test/abstract_unit.rb @@ -9,8 +9,8 @@ require "active_support/core_ext/kernel/reporting" # These are the normal settings that will be set up by Railties # TODO: Have these tests support other combinations of these values silence_warnings do - Encoding.default_internal = "UTF-8" - Encoding.default_external = "UTF-8" + Encoding.default_internal = Encoding::UTF_8 + Encoding.default_external = Encoding::UTF_8 end require "active_support/testing/autorun" @@ -163,7 +163,7 @@ class ActionDispatch::IntegrationTest < ActiveSupport::TestCase # Stub Rails dispatcher so it does not get controller references and # simply return the controller#action as Rack::Body. class StubDispatcher < ::ActionDispatch::Routing::RouteSet::Dispatcher - protected + private def controller_reference(controller_param) controller_param end diff --git a/actionview/test/activerecord/form_helper_activerecord_test.rb b/actionview/test/activerecord/form_helper_activerecord_test.rb index 6152ec4720..0f7960b408 100644 --- a/actionview/test/activerecord/form_helper_activerecord_test.rb +++ b/actionview/test/activerecord/form_helper_activerecord_test.rb @@ -52,7 +52,7 @@ class FormHelperActiveRecordTest < ActionView::TestCase assert_dom_equal expected, output_buffer end - protected + private def hidden_fields(method = nil) txt = %{<input name="utf8" type="hidden" value="✓" />} diff --git a/actionview/test/activerecord/polymorphic_routes_test.rb b/actionview/test/activerecord/polymorphic_routes_test.rb index 8495949975..fb670e5dbe 100644 --- a/actionview/test/activerecord/polymorphic_routes_test.rb +++ b/actionview/test/activerecord/polymorphic_routes_test.rb @@ -61,7 +61,7 @@ end class PolymorphicRoutesTest < ActionController::TestCase include SharedTestRoutes.url_helpers - self.default_url_options[:host] = "example.com" + default_url_options[:host] = "example.com" def setup @project = Project.new diff --git a/actionview/test/lib/controller/fake_models.rb b/actionview/test/lib/controller/fake_models.rb index 80649db88b..ddc915895d 100644 --- a/actionview/test/lib/controller/fake_models.rb +++ b/actionview/test/lib/controller/fake_models.rb @@ -80,7 +80,7 @@ class Comment def to_key; id ? [id] : nil end def save; @id = 1; @post_id = 1 end def persisted?; @id.present? end - def to_param; @id.to_s; end + def to_param; @id && @id.to_s; end def name @id.nil? ? "new #{self.class.name.downcase}" : "#{self.class.name.downcase} ##{@id}" end @@ -101,7 +101,7 @@ class Tag def to_key; id ? [id] : nil end def save; @id = 1; @post_id = 1 end def persisted?; @id.present? end - def to_param; @id; end + def to_param; @id && @id.to_s; end def value @id.nil? ? "new #{self.class.name.downcase}" : "#{self.class.name.downcase} ##{@id}" end @@ -120,7 +120,7 @@ class CommentRelevance def to_key; id ? [id] : nil end def save; @id = 1; @comment_id = 1 end def persisted?; @id.present? end - def to_param; @id; end + def to_param; @id && @id.to_s; end def value @id.nil? ? "new #{self.class.name.downcase}" : "#{self.class.name.downcase} ##{@id}" end @@ -136,7 +136,7 @@ class TagRelevance def to_key; id ? [id] : nil end def save; @id = 1; @tag_id = 1 end def persisted?; @id.present? end - def to_param; @id; end + def to_param; @id && @id.to_s; end def value @id.nil? ? "new #{self.class.name.downcase}" : "#{self.class.name.downcase} ##{@id}" end diff --git a/actionview/test/template/asset_tag_helper_test.rb b/actionview/test/template/asset_tag_helper_test.rb index 3bdab42f7a..07a6452cc1 100644 --- a/actionview/test/template/asset_tag_helper_test.rb +++ b/actionview/test/template/asset_tag_helper_test.rb @@ -630,7 +630,7 @@ class AssetTagHelperNonVhostTest < ActionView::TestCase end def test_should_return_nothing_if_asset_host_isnt_configured - assert_equal nil, compute_asset_host("foo") + assert_nil compute_asset_host("foo") end def test_should_current_request_host_is_always_returned_for_request diff --git a/actionview/test/template/capture_helper_test.rb b/actionview/test/template/capture_helper_test.rb index 54bf9b4c33..7f37523eeb 100644 --- a/actionview/test/template/capture_helper_test.rb +++ b/actionview/test/template/capture_helper_test.rb @@ -127,18 +127,18 @@ class CaptureHelperTest < ActionView::TestCase def test_content_for_returns_nil_when_writing assert ! content_for?(:title) - assert_equal nil, content_for(:title, "foo") - assert_equal nil, content_for(:title) { output_buffer << "bar"; nil } - assert_equal nil, content_for(:title) { output_buffer << " \n "; nil } + assert_nil content_for(:title, "foo") + assert_nil content_for(:title) { output_buffer << "bar"; nil } + assert_nil content_for(:title) { output_buffer << " \n "; nil } assert_equal "foobar", content_for(:title) - assert_equal nil, content_for(:title, "foo", flush: true) - assert_equal nil, content_for(:title, flush: true) { output_buffer << "bar"; nil } - assert_equal nil, content_for(:title, flush: true) { output_buffer << " \n "; nil } + assert_nil content_for(:title, "foo", flush: true) + assert_nil content_for(:title, flush: true) { output_buffer << "bar"; nil } + assert_nil content_for(:title, flush: true) { output_buffer << " \n "; nil } assert_equal "bar", content_for(:title) end def test_content_for_returns_nil_when_content_missing - assert_equal nil, content_for(:some_missing_key) + assert_nil content_for(:some_missing_key) end def test_content_for_question_mark diff --git a/actionview/test/template/form_helper/form_with_test.rb b/actionview/test/template/form_helper/form_with_test.rb index c078b47e14..3a91c7dce7 100644 --- a/actionview/test/template/form_helper/form_with_test.rb +++ b/actionview/test/template/form_helper/form_with_test.rb @@ -323,6 +323,75 @@ class FormWithActsLikeFormForTest < FormWithTest assert_dom_equal expected, output_buffer end + def test_form_with_only_url_on_create + form_with(url: "/posts") do |f| + concat f.label :title, "Label me" + concat f.text_field :title + end + + expected = whole_form("/posts") do + '<label for="title">Label me</label>' + + '<input type="text" name="title">' + end + + assert_dom_equal expected, output_buffer + end + + def test_form_with_only_url_on_update + form_with(url: "/posts/123") do |f| + concat f.label :title, "Label me" + concat f.text_field :title + end + + expected = whole_form("/posts/123") do + '<label for="title">Label me</label>' + + '<input type="text" name="title">' + end + + assert_dom_equal expected, output_buffer + end + + def test_form_with_general_attributes + form_with(url: "/posts/123") do |f| + concat f.text_field :no_model_to_back_this_badboy + end + + expected = whole_form("/posts/123") do + '<input type="text" name="no_model_to_back_this_badboy">' + end + + assert_dom_equal expected, output_buffer + end + + def test_form_with_attribute_not_on_model + form_with(model: @post) do |f| + concat f.text_field :this_dont_exist_on_post + end + + expected = whole_form("/posts/123", method: :patch) do + '<input type="text" name="post[this_dont_exist_on_post]">' + end + + assert_dom_equal expected, output_buffer + end + + def test_form_with_doesnt_call_private_or_protected_properties_on_form_object_skipping_value + obj = Class.new do + private def private_property + "That would be great." + end + + protected def protected_property + "I believe you have my stapler." + end + end.new + + form_with(model: obj, scope: "other_name", url: "/", id: "edit-other-name") do |f| + assert_dom_equal '<input type="hidden" name="other_name[private_property]">', f.hidden_field(:private_property) + assert_dom_equal '<input type="hidden" name="other_name[protected_property]">', f.hidden_field(:protected_property) + end + end + def test_form_with_with_collection_radio_buttons post = Post.new def post.active; false; end @@ -509,8 +578,6 @@ class FormWithActsLikeFormForTest < FormWithTest end def test_form_with_with_file_field_generate_multipart - Post.send :attr_accessor, :file - form_with(model: @post, id: "create-post") do |f| concat f.file_field(:file) end @@ -523,8 +590,6 @@ class FormWithActsLikeFormForTest < FormWithTest end def test_fields_with_file_field_generate_multipart - Comment.send :attr_accessor, :file - form_with(model: @post) do |f| concat f.fields(:comment, model: @post) { |c| concat c.file_field(:file) @@ -599,18 +664,6 @@ class FormWithActsLikeFormForTest < FormWithTest assert_dom_equal expected, output_buffer end - def test_form_tags_do_not_call_private_properties_on_form_object - obj = Class.new do - private def private_property - raise "This method should not be called." - end - end.new - - form_with(model: obj, scope: "other_name", url: "/", id: "edit-other-name") do |f| - assert_raise(NoMethodError) { f.hidden_field(:private_property) } - end - end - def test_form_with_with_method_as_part_of_html_options form_with(model: @post, url: "/", id: "create-post", html: { method: :delete }) do |f| concat f.text_field(:title) @@ -878,6 +931,40 @@ class FormWithActsLikeFormForTest < FormWithTest end end + def test_fields_with_attributes_not_on_model + form_with(model: @post) do |f| + concat f.fields(:comment) { |c| + concat c.text_field :dont_exist_on_model + } + end + + expected = whole_form("/posts/123", method: :patch) do + '<input type="text" name="post[comment][dont_exist_on_model]">' + end + + assert_dom_equal expected, output_buffer + end + + def test_fields_with_attributes_not_on_model_deep_nested + @comment.save + form_with(scope: :posts) do |f| + f.fields("post[]", model: @post) do |f2| + f2.text_field(:id) + @post.comments.each do |comment| + concat f2.fields("comment[]", model: comment) { |c| + concat c.text_field(:dont_exist_on_model) + } + end + end + end + + expected = whole_form do + '<input name="posts[post][0][comment][1][dont_exist_on_model]" type="text">' + end + + assert_dom_equal expected, output_buffer + end + def test_nested_fields @comment.body = "Hello World" form_with(model: @post) do |f| @@ -2096,7 +2183,7 @@ class FormWithActsLikeFormForTest < FormWithTest assert_equal 1, initialization_count, "form builder instantiated more than once" end - protected + private def hidden_fields(options = {}) method = options[:method] diff --git a/actionview/test/template/form_helper_test.rb b/actionview/test/template/form_helper_test.rb index 022bf315ce..2bc0434771 100644 --- a/actionview/test/template/form_helper_test.rb +++ b/actionview/test/template/form_helper_test.rb @@ -1751,8 +1751,6 @@ class FormHelperTest < ActionView::TestCase end def test_form_for_with_file_field_generate_multipart - Post.send :attr_accessor, :file - form_for(@post, html: { id: "create-post" }) do |f| concat f.file_field(:file) end @@ -1765,8 +1763,6 @@ class FormHelperTest < ActionView::TestCase end def test_fields_for_with_file_field_generate_multipart - Comment.send :attr_accessor, :file - form_for(@post) do |f| concat f.fields_for(:comment, @post) { |c| concat c.file_field(:file) @@ -1833,9 +1829,9 @@ class FormHelperTest < ActionView::TestCase obj = Class.new do private - def private_property - raise "This method should not be called." - end + def private_property + raise "This method should not be called." + end end.new form_for(obj, as: "other_name", url: "/", html: { id: "edit-other-name" }) do |f| @@ -2266,11 +2262,13 @@ class FormHelperTest < ActionView::TestCase concat f.fields_for("comment[]", @comment) { |c| concat c.text_field(:name) } + concat f.text_field(:body) end expected = whole_form("/posts/123", "edit_post[]", "edit_post[]", method: "patch") do "<input name='post[123][title]' type='text' id='post_123_title' value='Hello World' />" + - "<input name='post[123][comment][][name]' type='text' id='post_123_comment__name' value='new comment' />" + "<input name='post[123][comment][][name]' type='text' id='post_123_comment__name' value='new comment' />" + + "<input name='post[123][body]' type='text' id='post_123_body' value='Back to the hill and over it again!' />" end assert_dom_equal expected, output_buffer @@ -3443,7 +3441,7 @@ class FormHelperTest < ActionView::TestCase assert_equal 1, initialization_count, "form builder instantiated more than once" end - protected + private def hidden_fields(options = {}) method = options[:method] diff --git a/actionview/test/template/form_tag_helper_test.rb b/actionview/test/template/form_tag_helper_test.rb index 24ae6b8b90..084c540139 100644 --- a/actionview/test/template/form_tag_helper_test.rb +++ b/actionview/test/template/form_tag_helper_test.rb @@ -510,6 +510,13 @@ class FormTagHelperTest < ActionView::TestCase ) end + def test_submit_tag_doesnt_have_data_disable_with_twice_with_hash + assert_equal( + %(<input type="submit" name="commit" value="Save" data-disable-with="Processing..." />), + submit_tag("Save", data: { disable_with: "Processing..." }) + ) + end + def test_submit_tag_with_symbol_value assert_dom_equal( %(<input data-disable-with="Save" name='commit' type="submit" value="Save" />), @@ -694,31 +701,31 @@ class FormTagHelperTest < ActionView::TestCase def test_text_area_tag_options_symbolize_keys_side_effects options = { option: "random_option" } text_area_tag "body", "hello world", options - assert_equal options, option: "random_option" + assert_equal({ option: "random_option" }, options) end def test_submit_tag_options_symbolize_keys_side_effects options = { option: "random_option" } submit_tag "submit value", options - assert_equal options, option: "random_option" + assert_equal({ option: "random_option" }, options) end def test_button_tag_options_symbolize_keys_side_effects options = { option: "random_option" } button_tag "button value", options - assert_equal options, option: "random_option" + assert_equal({ option: "random_option" }, options) end def test_image_submit_tag_options_symbolize_keys_side_effects options = { option: "random_option" } image_submit_tag "submit source", options - assert_equal options, option: "random_option" + assert_equal({ option: "random_option" }, options) end def test_image_label_tag_options_symbolize_keys_side_effects options = { option: "random_option" } label_tag "submit source", "title", options - assert_equal options, option: "random_option" + assert_equal({ option: "random_option" }, options) end def protect_against_forgery? diff --git a/actionview/test/template/number_helper_test.rb b/actionview/test/template/number_helper_test.rb index 2a2ada2b36..678120a9c9 100644 --- a/actionview/test/template/number_helper_test.rb +++ b/actionview/test/template/number_helper_test.rb @@ -4,7 +4,7 @@ class NumberHelperTest < ActionView::TestCase tests ActionView::Helpers::NumberHelper def test_number_to_phone - assert_equal nil, number_to_phone(nil) + assert_nil number_to_phone(nil) assert_equal "555-1234", number_to_phone(5551234) assert_equal "(800) 555-1212 x 123", number_to_phone(8005551212, area_code: true, extension: 123) assert_equal "+18005551212", number_to_phone(8005551212, country_code: 1, delimiter: "") @@ -13,7 +13,7 @@ class NumberHelperTest < ActionView::TestCase end def test_number_to_currency - assert_equal nil, number_to_currency(nil) + assert_nil number_to_currency(nil) assert_equal "$1,234,567,890.50", number_to_currency(1234567890.50) assert_equal "$1,234,567,892", number_to_currency(1234567891.50, precision: 0) assert_equal "1,234,567,890.50 - Kč", number_to_currency("-1234567890.50", unit: raw("Kč"), format: "%n %u", negative_format: "%n - %u") @@ -25,7 +25,7 @@ class NumberHelperTest < ActionView::TestCase end def test_number_to_percentage - assert_equal nil, number_to_percentage(nil) + assert_nil number_to_percentage(nil) assert_equal "100.000%", number_to_percentage(100) assert_equal "100.000 %", number_to_percentage(100, format: "%n %") assert_equal "<b>100.000</b> %", number_to_percentage(100, format: "<b>%n</b> %") @@ -43,13 +43,13 @@ class NumberHelperTest < ActionView::TestCase end def test_number_with_delimiter - assert_equal nil, number_with_delimiter(nil) + assert_nil number_with_delimiter(nil) assert_equal "12,345,678", number_with_delimiter(12345678) assert_equal "0", number_with_delimiter(0) end def test_number_with_precision - assert_equal nil, number_with_precision(nil) + assert_nil number_with_precision(nil) assert_equal "-111.235", number_with_precision(-111.2346) assert_equal "111.00", number_with_precision(111, precision: 2) assert_equal "0.00100", number_with_precision(0.001, precision: 5) @@ -57,13 +57,13 @@ class NumberHelperTest < ActionView::TestCase end def test_number_to_human_size - assert_equal nil, number_to_human_size(nil) + assert_nil number_to_human_size(nil) assert_equal "3 Bytes", number_to_human_size(3.14159265) assert_equal "1.2 MB", number_to_human_size(1234567, precision: 2) end def test_number_to_human - assert_equal nil, number_to_human(nil) + assert_nil number_to_human(nil) assert_equal "0", number_to_human(0) assert_equal "1.23 Thousand", number_to_human(1234) assert_equal "489.0 Thousand", number_to_human(489000, precision: 4, strip_insignificant_zeros: false) diff --git a/actionview/test/template/render_test.rb b/actionview/test/template/render_test.rb index e7e0b147c7..2651d6ff73 100644 --- a/actionview/test/template/render_test.rb +++ b/actionview/test/template/render_test.rb @@ -475,11 +475,9 @@ module RenderTestCases end def test_render_ignores_templates_with_malformed_template_handlers - ActiveSupport::Deprecation.silence do - %w(malformed malformed.erb malformed.html.erb malformed.en.html.erb).each do |name| - assert File.exist?(File.expand_path("#{FIXTURE_LOAD_PATH}/test/malformed/#{name}~")), "Malformed file (#{name}~) which should be ignored does not exists" - assert_raises(ActionView::MissingTemplate) { @view.render(file: "test/malformed/#{name}") } - end + %w(malformed malformed.erb malformed.html.erb malformed.en.html.erb).each do |name| + assert File.exist?(File.expand_path("#{FIXTURE_LOAD_PATH}/test/malformed/#{name}~")), "Malformed file (#{name}~) which should be ignored does not exists" + assert_raises(ActionView::MissingTemplate) { @view.render(file: "test/malformed/#{name}") } end end diff --git a/actionview/test/template/test_case_test.rb b/actionview/test/template/test_case_test.rb index 41225000f0..3deddd5706 100644 --- a/actionview/test/template/test_case_test.rb +++ b/actionview/test/template/test_case_test.rb @@ -50,7 +50,7 @@ module ActionView end test "retrieve non existing config values" do - assert_equal nil, ActionView::Base.new.config.something_odd + assert_nil ActionView::Base.new.config.something_odd end test "works without testing a helper module" do diff --git a/actionview/test/template/url_helper_test.rb b/actionview/test/template/url_helper_test.rb index 5a2319fe96..95bea21c8f 100644 --- a/actionview/test/template/url_helper_test.rb +++ b/actionview/test/template/url_helper_test.rb @@ -235,14 +235,14 @@ class UrlHelperTest < ActiveSupport::TestCase end end - def test_button_to_with_permited_strong_params + def test_button_to_with_permitted_strong_params assert_dom_equal( %{<form action="http://www.example.com" class="button_to" method="post"><input type="submit" value="Hello" /><input type="hidden" name="baz" value="quux" /><input type="hidden" name="foo" value="bar" /></form>}, button_to("Hello", "http://www.example.com", params: FakeParams.new) ) end - def test_button_to_with_unpermited_strong_params + def test_button_to_with_unpermitted_strong_params assert_raises(ArgumentError) do button_to("Hello", "http://www.example.com", params: FakeParams.new(false)) end @@ -496,6 +496,15 @@ class UrlHelperTest < ActiveSupport::TestCase assert current_page?("http://www.example.com/") end + def test_current_page_considering_params + @request = request_for_url("/?order=desc&page=1") + + assert !current_page?(url_hash, check_parameters: true) + assert !current_page?(url_hash.merge(check_parameters: true)) + assert !current_page?(ActionController::Parameters.new(url_hash.merge(check_parameters: true)).permit!) + assert !current_page?("http://www.example.com/", check_parameters: true) + end + def test_current_page_with_params_that_match @request = request_for_url("/?order=desc&page=1") @@ -810,7 +819,7 @@ class TasksController < ActionController::Base render_default end - protected + private def render_default render inline: "<%= link_to_unless_current('tasks', tasks_path) %>\n" + "<%= link_to_unless_current('tasks', tasks_url) %>" diff --git a/actionview/test/ujs/config.ru b/actionview/test/ujs/config.ru index cb961dc140..414c2063c3 100644 --- a/actionview/test/ujs/config.ru +++ b/actionview/test/ujs/config.ru @@ -1,3 +1,3 @@ -$LOAD_PATH.unshift File.expand_path('..', __FILE__) -require 'server' +$LOAD_PATH.unshift File.expand_path("..", __FILE__) +require "server" run UJS::Server diff --git a/actionview/test/ujs/server.rb b/actionview/test/ujs/server.rb index cc02cd8419..25f70baf5f 100644 --- a/actionview/test/ujs/server.rb +++ b/actionview/test/ujs/server.rb @@ -12,7 +12,7 @@ module UJS get "/rails-ujs.js" => Blade::Assets.environment get "/" => "tests#index" match "/echo" => "tests#echo", via: :all - get "/error" => proc {|env| [403, {}, []] } + get "/error" => proc { |env| [403, {}, []] } end config.cache_classes = false @@ -26,7 +26,7 @@ module UJS end module TestsHelper - def jquery_link version + def jquery_link(version) if params[:version] == version "[#{version}]" else @@ -34,7 +34,7 @@ module TestsHelper end end - def cdn_link cdn + def cdn_link(cdn) if params[:cdn] == cdn "[#{cdn}]" else @@ -43,22 +43,22 @@ module TestsHelper end def jquery_src - if params[:version] == 'edge' + if params[:version] == "edge" "/vendor/jquery.js" - elsif params[:cdn] && params[:cdn] == 'googleapis' + elsif params[:cdn] && params[:cdn] == "googleapis" "https://ajax.googleapis.com/ajax/libs/jquery/#{params[:version]}/jquery.min.js" else "http://code.jquery.com/jquery-#{params[:version]}.js" end end - def test_to *names + def test_to(*names) names = ["/vendor/qunit.js", "settings"] + names names.map { |name| script_tag name }.join("\n").html_safe end - def script_tag src - src = "/test/#{src}.js" unless src.index('/') + def script_tag(src) + src = "/test/#{src}.js" unless src.index("/") %(<script src="#{src}" type="text/javascript"></script>).html_safe end @@ -72,20 +72,20 @@ class TestsController < ActionController::Base layout "application" def index - params[:version] ||= ENV['JQUERY_VERSION'] || '1.11.0' - params[:cdn] ||= 'jquery' + params[:version] ||= ENV["JQUERY_VERSION"] || "1.11.0" + params[:cdn] ||= "jquery" render :index end def echo - data = { :params => params.to_unsafe_h }.update(request.env) + data = { params: params.to_unsafe_h }.update(request.env) - if params[:content_type] and params[:content] + if params[:content_type] && params[:content] render inline: params[:content], content_type: params[:content_type] elsif request.xhr? render json: JSON.generate(data) elsif params[:iframe] - payload = JSON.generate(data).gsub('<', '<').gsub('>', '>') + payload = JSON.generate(data).gsub("<", "<").gsub(">", ">") html = <<-HTML <script> if (window.top && window.top !== window) diff --git a/activejob/MIT-LICENSE b/activejob/MIT-LICENSE index a3ffb46b3f..daa726b9f0 100644 --- a/activejob/MIT-LICENSE +++ b/activejob/MIT-LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2014-2016 David Heinemeier Hansson +Copyright (c) 2014-2017 David Heinemeier Hansson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/activejob/Rakefile b/activejob/Rakefile index 3953116061..41ff76135e 100644 --- a/activejob/Rakefile +++ b/activejob/Rakefile @@ -1,8 +1,9 @@ require "rake/testtask" #TODO: add qu back to the list after it support Rails 5.1 -ACTIVEJOB_ADAPTERS = %w(async inline delayed_job que queue_classic resque sidekiq sneakers sucker_punch backburner test) -ACTIVEJOB_ADAPTERS -= %w(queue_classic) if defined?(JRUBY_VERSION) +#TODO: add delayed_job back to the list after it support Rails 5.1 +ACTIVEJOB_ADAPTERS = %w(async inline que queue_classic resque sidekiq sneakers sucker_punch backburner test) +ACTIVEJOB_ADAPTERS.delete("queue_classic") if defined?(JRUBY_VERSION) task default: :test task test: "test:default" diff --git a/activejob/lib/active_job.rb b/activejob/lib/active_job.rb index 20ca7085c6..8b7aef65a2 100644 --- a/activejob/lib/active_job.rb +++ b/activejob/lib/active_job.rb @@ -1,5 +1,5 @@ #-- -# Copyright (c) 2014-2016 David Heinemeier Hansson +# Copyright (c) 2014-2017 David Heinemeier Hansson # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the diff --git a/activejob/lib/active_job/enqueuing.rb b/activejob/lib/active_job/enqueuing.rb index 78eca1d2a1..c73117e7f3 100644 --- a/activejob/lib/active_job/enqueuing.rb +++ b/activejob/lib/active_job/enqueuing.rb @@ -18,8 +18,8 @@ module ActiveJob job_or_instantiate(*args).enqueue end - protected - def job_or_instantiate(*args) + private + def job_or_instantiate(*args) # :doc: args.first.is_a?(self) ? args.first : new(*args) end end diff --git a/activejob/lib/active_job/test_helper.rb b/activejob/lib/active_job/test_helper.rb index e6334d14ea..d01795f0c5 100644 --- a/activejob/lib/active_job/test_helper.rb +++ b/activejob/lib/active_job/test_helper.rb @@ -315,15 +315,15 @@ module ActiveJob end private - def clear_enqueued_jobs # :nodoc: + def clear_enqueued_jobs enqueued_jobs.clear end - def clear_performed_jobs # :nodoc: + def clear_performed_jobs performed_jobs.clear end - def enqueued_jobs_size(only: nil) # :nodoc: + def enqueued_jobs_size(only: nil) if only enqueued_jobs.count { |job| Array(only).include?(job.fetch(:job)) } else @@ -331,14 +331,14 @@ module ActiveJob end end - def serialize_args_for_assertion(args) # :nodoc: + def serialize_args_for_assertion(args) args.dup.tap do |serialized_args| serialized_args[:args] = ActiveJob::Arguments.serialize(serialized_args[:args]) if serialized_args[:args] serialized_args[:at] = serialized_args[:at].to_f if serialized_args[:at] end end - def instantiate_job(payload) # :nodoc: + def instantiate_job(payload) job = payload[:job].new(*payload[:args]) job.scheduled_at = Time.at(payload[:at]) if payload.key?(:at) job.queue_name = payload[:queue] diff --git a/activejob/lib/rails/generators/job/job_generator.rb b/activejob/lib/rails/generators/job/job_generator.rb index 97c11a9ea6..50476a2e50 100644 --- a/activejob/lib/rails/generators/job/job_generator.rb +++ b/activejob/lib/rails/generators/job/job_generator.rb @@ -19,7 +19,7 @@ module Rails # :nodoc: template "job.rb", File.join("app/jobs", class_path, "#{file_name}_job.rb") in_root do - if self.behavior == :invoke && !File.exist?(application_job_file_name) + if behavior == :invoke && !File.exist?(application_job_file_name) template "application_job.rb", application_job_file_name end end diff --git a/activejob/test/cases/queue_naming_test.rb b/activejob/test/cases/queue_naming_test.rb index 0e1e0decd1..0247bc111e 100644 --- a/activejob/test/cases/queue_naming_test.rb +++ b/activejob/test/cases/queue_naming_test.rb @@ -56,7 +56,7 @@ class QueueNamingTest < ActiveSupport::TestCase original_queue_name = HelloJob.queue_name begin - HelloJob.queue_as { self.arguments.first == "1" ? :one : :two } + HelloJob.queue_as { arguments.first == "1" ? :one : :two } assert_equal "one", HelloJob.new("1").queue_name assert_equal "two", HelloJob.new("3").queue_name ensure diff --git a/activejob/test/cases/queue_priority_test.rb b/activejob/test/cases/queue_priority_test.rb index ec78a402d7..171fb1e593 100644 --- a/activejob/test/cases/queue_priority_test.rb +++ b/activejob/test/cases/queue_priority_test.rb @@ -3,7 +3,7 @@ require "jobs/hello_job" class QueuePriorityTest < ActiveSupport::TestCase test "priority unset by default" do - assert_equal nil, HelloJob.priority + assert_nil HelloJob.priority end test "uses given priority" do @@ -32,7 +32,7 @@ class QueuePriorityTest < ActiveSupport::TestCase original_priority = HelloJob.priority begin - HelloJob.queue_with_priority { self.arguments.first == "1" ? 99 : 11 } + HelloJob.queue_with_priority { arguments.first == "1" ? 99 : 11 } assert_equal 99, HelloJob.new("1").priority assert_equal 11, HelloJob.new("3").priority ensure diff --git a/activejob/test/cases/test_helper_test.rb b/activejob/test/cases/test_helper_test.rb index 685c93da2d..51fc6cc0c4 100644 --- a/activejob/test/cases/test_helper_test.rb +++ b/activejob/test/cases/test_helper_test.rb @@ -256,11 +256,11 @@ end class PerformedJobsTest < ActiveJob::TestCase def test_performed_enqueue_jobs_with_only_option_doesnt_leak_outside_the_block - assert_equal nil, queue_adapter.filter + assert_nil queue_adapter.filter perform_enqueued_jobs only: HelloJob do assert_equal HelloJob, queue_adapter.filter end - assert_equal nil, queue_adapter.filter + assert_nil queue_adapter.filter end def test_assert_performed_jobs diff --git a/activejob/test/helper.rb b/activejob/test/helper.rb index 758506b3c0..776f7788de 100644 --- a/activejob/test/helper.rb +++ b/activejob/test/helper.rb @@ -5,6 +5,7 @@ ActiveSupport.halt_callback_chains_on_return_false = false GlobalID.app = "aj" @adapter = ENV["AJ_ADAPTER"] || "inline" +puts "Using #{@adapter}" if ENV["AJ_INTEGRATION_TESTS"] require "support/integration/helper" diff --git a/activejob/test/jobs/application_job.rb b/activejob/test/jobs/application_job.rb index 4a78b890ec..a009ace51c 100644 --- a/activejob/test/jobs/application_job.rb +++ b/activejob/test/jobs/application_job.rb @@ -1,4 +1,2 @@ -require_relative "../support/job_buffer" - class ApplicationJob < ActiveJob::Base end diff --git a/activejob/test/support/integration/adapters/sneakers.rb b/activejob/test/support/integration/adapters/sneakers.rb index 911f70407e..1c8dfaca59 100644 --- a/activejob/test/support/integration/adapters/sneakers.rb +++ b/activejob/test/support/integration/adapters/sneakers.rb @@ -73,7 +73,7 @@ module SneakersJobsManager true end - protected + private def bunny_publisher @bunny_publisher ||= begin p = ActiveJob::QueueAdapters::SneakersAdapter::JobWrapper.send(:publisher) diff --git a/activejob/test/support/integration/test_case_helpers.rb b/activejob/test/support/integration/test_case_helpers.rb index 3357489f20..41bf9c89d1 100644 --- a/activejob/test/support/integration/test_case_helpers.rb +++ b/activejob/test/support/integration/test_case_helpers.rb @@ -17,7 +17,7 @@ module TestCaseHelpers end end - protected + private def jobs_manager JobsManager.current_manager diff --git a/activejob/test/support/sneakers/inline.rb b/activejob/test/support/sneakers/inline.rb index 3cdc54e6d5..cf102ae5c2 100644 --- a/activejob/test/support/sneakers/inline.rb +++ b/activejob/test/support/sneakers/inline.rb @@ -4,7 +4,7 @@ module Sneakers module Worker module ClassMethods def enqueue(msg) - worker = self.new(nil, nil, {}) + worker = new(nil, nil, {}) worker.work(*msg) end end diff --git a/activemodel/MIT-LICENSE b/activemodel/MIT-LICENSE index 8573eb1225..ac810e86d0 100644 --- a/activemodel/MIT-LICENSE +++ b/activemodel/MIT-LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2016 David Heinemeier Hansson +Copyright (c) 2004-2017 David Heinemeier Hansson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb index a9b0663940..8163856a46 100644 --- a/activemodel/lib/active_model.rb +++ b/activemodel/lib/active_model.rb @@ -1,5 +1,5 @@ #-- -# Copyright (c) 2004-2016 David Heinemeier Hansson +# Copyright (c) 2004-2017 David Heinemeier Hansson # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb index 1441b146f8..09825cf861 100644 --- a/activemodel/lib/active_model/attribute_methods.rb +++ b/activemodel/lib/active_model/attribute_methods.rb @@ -334,12 +334,11 @@ module ActiveModel }.tap { |mod| include mod } end - protected - def instance_method_already_implemented?(method_name) #:nodoc: + private + def instance_method_already_implemented?(method_name) generated_attribute_methods.method_defined?(method_name) end - private # The methods +method_missing+ and +respond_to?+ of this module are # invoked often in a typical rails, both of which invoke the method # +matched_attribute_method+. The latter method iterates through an @@ -349,11 +348,11 @@ module ActiveModel # used to alleviate the GC, which ultimately also speeds up the app # significantly (in our case our test suite finishes 10% faster with # this cache). - def attribute_method_matchers_cache #:nodoc: + def attribute_method_matchers_cache @attribute_method_matchers_cache ||= Concurrent::Map.new(initial_capacity: 4) end - def attribute_method_matchers_matching(method_name) #:nodoc: + def attribute_method_matchers_matching(method_name) attribute_method_matchers_cache.compute_if_absent(method_name) do # Must try to match prefixes/suffixes first, or else the matcher with no prefix/suffix # will match every time. @@ -365,7 +364,7 @@ module ActiveModel # Define a method `name` in `mod` that dispatches to `send` # using the given `extra` args. This falls back on `define_method` # and `send` if the given names cannot be compiled. - def define_proxy_call(include_private, mod, name, send, *extra) #:nodoc: + def define_proxy_call(include_private, mod, name, send, *extra) defn = if NAME_COMPILABLE_REGEXP.match?(name) "def #{name}(*args)" else @@ -458,12 +457,11 @@ module ActiveModel end end - protected - def attribute_method?(attr_name) #:nodoc: + private + def attribute_method?(attr_name) respond_to_without_attributes?(:attributes) && attributes.include?(attr_name) end - private # Returns a struct representing the matching attribute method. # The struct's attributes are prefix, base and suffix. def matched_attribute_method(method_name) diff --git a/activemodel/lib/active_model/callbacks.rb b/activemodel/lib/active_model/callbacks.rb index 283090e380..e99bfab6fd 100644 --- a/activemodel/lib/active_model/callbacks.rb +++ b/activemodel/lib/active_model/callbacks.rb @@ -122,19 +122,19 @@ module ActiveModel private - def _define_before_model_callback(klass, callback) #:nodoc: + def _define_before_model_callback(klass, callback) klass.define_singleton_method("before_#{callback}") do |*args, &block| set_callback(:"#{callback}", :before, *args, &block) end end - def _define_around_model_callback(klass, callback) #:nodoc: + def _define_around_model_callback(klass, callback) klass.define_singleton_method("around_#{callback}") do |*args, &block| set_callback(:"#{callback}", :around, *args, &block) end end - def _define_after_model_callback(klass, callback) #:nodoc: + def _define_after_model_callback(klass, callback) klass.define_singleton_method("after_#{callback}") do |*args, &block| options = args.extract_options! options[:prepend] = true diff --git a/activemodel/lib/active_model/forbidden_attributes_protection.rb b/activemodel/lib/active_model/forbidden_attributes_protection.rb index d2c6a89cc2..45ab8a2ca1 100644 --- a/activemodel/lib/active_model/forbidden_attributes_protection.rb +++ b/activemodel/lib/active_model/forbidden_attributes_protection.rb @@ -15,7 +15,7 @@ module ActiveModel end module ForbiddenAttributesProtection # :nodoc: - protected + private def sanitize_for_mass_assignment(attributes) if attributes.respond_to?(:permitted?) raise ActiveModel::ForbiddenAttributesError if !attributes.permitted? diff --git a/activemodel/lib/active_model/naming.rb b/activemodel/lib/active_model/naming.rb index 9532c63b65..9853cf38fe 100644 --- a/activemodel/lib/active_model/naming.rb +++ b/activemodel/lib/active_model/naming.rb @@ -234,7 +234,7 @@ module ActiveModel # Person.model_name.plural # => "people" def model_name @_model_name ||= begin - namespace = self.parents.detect do |n| + namespace = parents.detect do |n| n.respond_to?(:use_relative_model_naming?) && n.use_relative_model_naming? end ActiveModel::Name.new(self, namespace) diff --git a/activemodel/lib/active_model/translation.rb b/activemodel/lib/active_model/translation.rb index b8cf43cc10..35fc7cf743 100644 --- a/activemodel/lib/active_model/translation.rb +++ b/activemodel/lib/active_model/translation.rb @@ -44,7 +44,7 @@ module ActiveModel parts = attribute.to_s.split(".") attribute = parts.pop namespace = parts.join("/") unless parts.empty? - attributes_scope = "#{self.i18n_scope}.attributes" + attributes_scope = "#{i18n_scope}.attributes" if namespace defaults = lookup_ancestors.map do |klass| diff --git a/activemodel/lib/active_model/type/helpers/accepts_multiparameter_time.rb b/activemodel/lib/active_model/type/helpers/accepts_multiparameter_time.rb index facea12704..f783d286c5 100644 --- a/activemodel/lib/active_model/type/helpers/accepts_multiparameter_time.rb +++ b/activemodel/lib/active_model/type/helpers/accepts_multiparameter_time.rb @@ -1,7 +1,7 @@ module ActiveModel module Type - module Helpers - class AcceptsMultiparameterTime < Module # :nodoc: + module Helpers # :nodoc: all + class AcceptsMultiparameterTime < Module def initialize(defaults: {}) define_method(:cast) do |value| if value.is_a?(Hash) diff --git a/activemodel/lib/active_model/type/helpers/mutable.rb b/activemodel/lib/active_model/type/helpers/mutable.rb index 4dddbe4e5e..f3a17a1698 100644 --- a/activemodel/lib/active_model/type/helpers/mutable.rb +++ b/activemodel/lib/active_model/type/helpers/mutable.rb @@ -1,7 +1,7 @@ module ActiveModel module Type - module Helpers - module Mutable # :nodoc: + module Helpers # :nodoc: all + module Mutable def cast(value) deserialize(serialize(value)) end diff --git a/activemodel/lib/active_model/type/helpers/numeric.rb b/activemodel/lib/active_model/type/helpers/numeric.rb index 98533f8771..275822b738 100644 --- a/activemodel/lib/active_model/type/helpers/numeric.rb +++ b/activemodel/lib/active_model/type/helpers/numeric.rb @@ -1,7 +1,7 @@ module ActiveModel module Type - module Helpers - module Numeric # :nodoc: + module Helpers # :nodoc: all + module Numeric def cast(value) value = \ case value diff --git a/activemodel/lib/active_model/type/helpers/time_value.rb b/activemodel/lib/active_model/type/helpers/time_value.rb index 721f9543ed..e57a52104b 100644 --- a/activemodel/lib/active_model/type/helpers/time_value.rb +++ b/activemodel/lib/active_model/type/helpers/time_value.rb @@ -2,8 +2,8 @@ require "active_support/core_ext/time/zones" module ActiveModel module Type - module Helpers - module TimeValue # :nodoc: + module Helpers # :nodoc: all + module TimeValue def serialize(value) value = apply_seconds_precision(value) diff --git a/activemodel/lib/active_model/type/integer.rb b/activemodel/lib/active_model/type/integer.rb index 41dd655a5c..230e45ba60 100644 --- a/activemodel/lib/active_model/type/integer.rb +++ b/activemodel/lib/active_model/type/integer.rb @@ -29,6 +29,8 @@ module ActiveModel result end + # TODO Change this to private once we've dropped Ruby 2.2 support. + # Workaround for Ruby 2.2 "private attribute?" warning. protected attr_reader :range diff --git a/activemodel/lib/active_model/type/registry.rb b/activemodel/lib/active_model/type/registry.rb index d25f1a129e..2d5dd366eb 100644 --- a/activemodel/lib/active_model/type/registry.rb +++ b/activemodel/lib/active_model/type/registry.rb @@ -21,6 +21,8 @@ module ActiveModel end end + # TODO Change this to private once we've dropped Ruby 2.2 support. + # Workaround for Ruby 2.2 "private attribute?" warning. protected attr_reader :registrations @@ -55,6 +57,8 @@ module ActiveModel type_name == name end + # TODO Change this to private once we've dropped Ruby 2.2 support. + # Workaround for Ruby 2.2 "private attribute?" warning. protected attr_reader :name, :block diff --git a/activemodel/lib/active_model/validations.rb b/activemodel/lib/active_model/validations.rb index a1f7c971db..d460068830 100644 --- a/activemodel/lib/active_model/validations.rb +++ b/activemodel/lib/active_model/validations.rb @@ -399,14 +399,14 @@ module ActiveModel # end alias :read_attribute_for_validation :send - protected + private - def run_validations! #:nodoc: + def run_validations! _run_validate_callbacks errors.empty? end - def raise_validation_error + def raise_validation_error # :doc: raise(ValidationError.new(self)) end end diff --git a/activemodel/lib/active_model/validations/acceptance.rb b/activemodel/lib/active_model/validations/acceptance.rb index 9826c2fe9c..e11005b9ba 100644 --- a/activemodel/lib/active_model/validations/acceptance.rb +++ b/activemodel/lib/active_model/validations/acceptance.rb @@ -56,6 +56,8 @@ module ActiveModel klass.send(:attr_writer, *attr_writers) end + # TODO Change this to private once we've dropped Ruby 2.2 support. + # Workaround for Ruby 2.2 "private attribute?" warning. protected attr_reader :attributes diff --git a/activemodel/lib/active_model/validations/callbacks.rb b/activemodel/lib/active_model/validations/callbacks.rb index a201f72ed0..70bc1a0624 100644 --- a/activemodel/lib/active_model/validations/callbacks.rb +++ b/activemodel/lib/active_model/validations/callbacks.rb @@ -104,10 +104,10 @@ module ActiveModel end end - protected + private # Overwrite run validations to include callbacks. - def run_validations! #:nodoc: + def run_validations! _run_validation_callbacks { super } end end diff --git a/activemodel/lib/active_model/validations/numericality.rb b/activemodel/lib/active_model/validations/numericality.rb index 48e0e5c9f6..2297faaee5 100644 --- a/activemodel/lib/active_model/validations/numericality.rb +++ b/activemodel/lib/active_model/validations/numericality.rb @@ -61,29 +61,29 @@ module ActiveModel end end - protected + private - def is_number?(raw_value) + def is_number?(raw_value) # :doc: !parse_raw_value_as_a_number(raw_value).nil? rescue ArgumentError, TypeError false end - def parse_raw_value_as_a_number(raw_value) + def parse_raw_value_as_a_number(raw_value) # :doc: Kernel.Float(raw_value) if raw_value !~ /\A0[xX]/ end - def is_integer?(raw_value) + def is_integer?(raw_value) # :doc: /\A[+-]?\d+\z/ === raw_value.to_s end - def filtered_options(value) + def filtered_options(value) # :doc: filtered = options.except(*RESERVED_OPTIONS) filtered[:value] = value filtered end - def allow_only_integer?(record) + def allow_only_integer?(record) # :doc: case options[:only_integer] when Symbol record.send(options[:only_integer]) @@ -94,12 +94,10 @@ module ActiveModel end end - private - - def record_attribute_changed_in_place?(record, attr_name) - record.respond_to?(:attribute_changed_in_place?) && - record.attribute_changed_in_place?(attr_name.to_s) - end + def record_attribute_changed_in_place?(record, attr_name) + record.respond_to?(:attribute_changed_in_place?) && + record.attribute_changed_in_place?(attr_name.to_s) + end end module HelperMethods diff --git a/activemodel/lib/active_model/validations/validates.rb b/activemodel/lib/active_model/validations/validates.rb index f95f44de61..0ce5935f3a 100644 --- a/activemodel/lib/active_model/validations/validates.rb +++ b/activemodel/lib/active_model/validations/validates.rb @@ -148,15 +148,15 @@ module ActiveModel validates(*(attributes << options)) end - protected + private # When creating custom validators, it might be useful to be able to specify # additional default keys. This can be done by overwriting this method. - def _validates_default_keys # :nodoc: + def _validates_default_keys [:if, :unless, :on, :allow_blank, :allow_nil , :strict] end - def _parse_validates_options(options) # :nodoc: + def _parse_validates_options(options) case options when TrueClass {} diff --git a/activemodel/lib/active_model/validator.rb b/activemodel/lib/active_model/validator.rb index 8212744170..98234e9b6b 100644 --- a/activemodel/lib/active_model/validator.rb +++ b/activemodel/lib/active_model/validator.rb @@ -141,8 +141,8 @@ module ActiveModel end # Performs validation on the supplied record. By default this will call - # +validates_each+ to determine validity therefore subclasses should - # override +validates_each+ with validation logic. + # +validate_each+ to determine validity therefore subclasses should + # override +validate_each+ with validation logic. def validate(record) attributes.each do |attribute| value = record.read_attribute_for_validation(attribute) diff --git a/activemodel/test/cases/attribute_assignment_test.rb b/activemodel/test/cases/attribute_assignment_test.rb index 8eb389d331..fa41d1b95f 100644 --- a/activemodel/test/cases/attribute_assignment_test.rb +++ b/activemodel/test/cases/attribute_assignment_test.rb @@ -16,6 +16,8 @@ class AttributeAssignmentTest < ActiveModel::TestCase raise ErrorFromAttributeWriter end + # TODO Change this to private once we've dropped Ruby 2.2 support. + # Workaround for Ruby 2.2 "private attribute?" warning. protected attr_writer :metadata diff --git a/activemodel/test/cases/secure_password_test.rb b/activemodel/test/cases/secure_password_test.rb index 9423df2c09..77ce86f392 100644 --- a/activemodel/test/cases/secure_password_test.rb +++ b/activemodel/test/cases/secure_password_test.rb @@ -179,7 +179,7 @@ class SecurePasswordTest < ActiveModel::TestCase test "setting a nil password should clear an existing password" do @existing_user.password = nil - assert_equal nil, @existing_user.password_digest + assert_nil @existing_user.password_digest end test "authenticate" do diff --git a/activemodel/test/cases/type/binary_test.rb b/activemodel/test/cases/type/binary_test.rb index e6a32dbeec..e9c2ccfca4 100644 --- a/activemodel/test/cases/type/binary_test.rb +++ b/activemodel/test/cases/type/binary_test.rb @@ -6,7 +6,7 @@ module ActiveModel class BinaryTest < ActiveModel::TestCase def test_type_cast_binary type = Type::Binary.new - assert_equal nil, type.cast(nil) + assert_nil type.cast(nil) assert_equal "1", type.cast("1") assert_equal 1, type.cast(1) end diff --git a/activemodel/test/cases/type/date_test.rb b/activemodel/test/cases/type/date_test.rb index 44e20a327b..0cc90e99d3 100644 --- a/activemodel/test/cases/type/date_test.rb +++ b/activemodel/test/cases/type/date_test.rb @@ -6,10 +6,10 @@ module ActiveModel class DateTest < ActiveModel::TestCase def test_type_cast_date type = Type::Date.new - assert_equal nil, type.cast(nil) - assert_equal nil, type.cast("") - assert_equal nil, type.cast(" ") - assert_equal nil, type.cast("ABC") + assert_nil type.cast(nil) + assert_nil type.cast("") + assert_nil type.cast(" ") + assert_nil type.cast("ABC") date_string = ::Time.now.utc.strftime("%F") assert_equal date_string, type.cast(date_string).strftime("%F") diff --git a/activemodel/test/cases/type/date_time_test.rb b/activemodel/test/cases/type/date_time_test.rb index fb82260d2b..75a7fc686e 100644 --- a/activemodel/test/cases/type/date_time_test.rb +++ b/activemodel/test/cases/type/date_time_test.rb @@ -6,10 +6,10 @@ module ActiveModel class DateTimeTest < ActiveModel::TestCase def test_type_cast_datetime_and_timestamp type = Type::DateTime.new - assert_equal nil, type.cast(nil) - assert_equal nil, type.cast("") - assert_equal nil, type.cast(" ") - assert_equal nil, type.cast("ABC") + assert_nil type.cast(nil) + assert_nil type.cast("") + assert_nil type.cast(" ") + assert_nil type.cast("ABC") datetime_string = ::Time.now.utc.strftime("%FT%T") assert_equal datetime_string, type.cast(datetime_string).strftime("%FT%T") diff --git a/activemodel/test/cases/type/time_test.rb b/activemodel/test/cases/type/time_test.rb index a6a79833e6..0cc4d33caa 100644 --- a/activemodel/test/cases/type/time_test.rb +++ b/activemodel/test/cases/type/time_test.rb @@ -6,9 +6,9 @@ module ActiveModel class TimeTest < ActiveModel::TestCase def test_type_cast_time type = Type::Time.new - assert_equal nil, type.cast(nil) - assert_equal nil, type.cast("") - assert_equal nil, type.cast("ABC") + assert_nil type.cast(nil) + assert_nil type.cast("") + assert_nil type.cast("ABC") time_string = ::Time.now.utc.strftime("%T") assert_equal time_string, type.cast(time_string).strftime("%T") diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index eac54e7566..18c9a248b2 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,10 +1,158 @@ +* Deprecate passing a class to the `class_name` because it eagerloads more classes than + necessary and potentially creates circular dependencies. + + *Kir Shatrov* + +* Raise error when has_many through is defined before through association + + Fixes #26834 + + *Chris Holmes* + +* Deprecate passing `name` to `indexes`. + + *Ryuta Kamizono* + +* Remove deprecated tasks: `db:test:clone`, `db:test:clone_schema`, `db:test:clone_structure`. + + *Rafel Mendonça França* + +* Compare deserialized values for `PostgreSQL::OID::Hstore` types when + calling `ActiveRecord::Dirty#changed_in_place?`. + + Fixes #27502. + + *Jon Moss* + +* Raise `ArgumentError` when passing an `ActiveRecord::Base` instance to `.find`, + `.exists?` and `.update`. + + *Rafael Mendonça França* + +* Respect precision option for arrays of timestamps. + + Fixes #27514. + + *Sean Griffin* + +* Optimize slow model instantiation when using STI and `store_full_sti_class = false` option. + + *Konstantin Lazarev* + +* Add `touch` option to counter cache modifying methods. + + Works when updating, resetting, incrementing and decrementing counters: + + # Touches `updated_at`/`updated_on`. + Topic.increment_counter(:messages_count, 1, touch: true) + Topic.decrement_counter(:messages_count, 1, touch: true) + + # Touches `last_discussed_at`. + Topic.reset_counters(18, :messages, touch: :last_discussed_at) + + # Touches `updated_at` and `last_discussed_at`. + Topic.update_counters(18, messages_count: 5, touch: %i( updated_at last_discussed_at )) + + Fixes #26724. + + *Jarred Trost* + +* Remove deprecated `#uniq`, `#uniq!`, and `#uniq_value`. + + *Ryuta Kamizono* + +* Remove deprecated `#insert_sql`, `#update_sql`, and `#delete_sql`. + + *Ryuta Kamizono* + +* Remove deprecated `#use_transactional_fixtures` configuration. + + *Rafael Mendonça França* + +* Remove deprecated `#raise_in_transactional_callbacks` configuration. + + *Rafael Mendonça França* + +* Remove deprecated `#load_schema_for`. + + *Rafael Mendonça França* + +* Remove deprecated conditions parameter from `#destroy_all` and `#delete_all`. + + *Rafael Mendonça França* + +* Remove deprecated support to passing arguments to `#select` when a block is provided. + + *Rafael Mendonça França* + +* Remove deprecated support to query using commas on LIMIT. + + *Rafael Mendonça França* + +* Remove deprecated support to passing a class as a value in a query. + + *Rafael Mendonça França* + +* Raise `ActiveRecord::IrreversibleOrderError` when using `last` with an irreversible + order. + + *Rafael Mendonça França* + +* Raise when a `has_many :through` association has an ambiguous reflection name. + + *Rafael Mendonça França* + +* Raise when `ActiveRecord::Migration` is inherited from directly. + + *Rafael Mendonça França* + +* Remove deprecated `original_exception` argument in `ActiveRecord::StatementInvalid#initialize` + and `ActiveRecord::StatementInvalid#original_exception`. + + *Rafael Mendonça França* + +* `#tables` and `#table_exists?` return only tables and not views. + + All the deprecations on those methods were removed. + + *Rafael Mendonça França* + +* Remove deprecated `name` argument from `#tables`. + + *Rafael Mendonça França* + +* Remove deprecated support to passing a column to `#quote`. + + *Rafael Mendonça França* + +* Set `:time` as a timezone aware type and remove deprecation when + `config.active_record.time_zone_aware_types` is not explictly set. + + *Rafael Mendonça França* + +* Remove deprecated force reload argument in singular and collection association readers. + + *Rafael Mendonça França* + +* Remove deprecated `activerecord.errors.messages.restrict_dependent_destroy.one` and + `activerecord.errors.messages.restrict_dependent_destroy.many` i18n scopes. + + *Rafael Mendonça França* + +* Allow passing extra flags to `db:structure:load` and `db:structure:dump` + + Introduces `ActiveRecord::Tasks::DatabaseTasks.structure_(load|dump)_flags` to customize the + eventual commands run against the database, e.g. mysqldump/pg_dump. + + *Kir Shatrov* + * Notifications see frozen SQL string. - Fixes #23774 + Fixes #23774. *Richard Monette* -* RuntimeErrors are no longer translated to ActiveRecord::StatementInvalid. +* RuntimeErrors are no longer translated to `ActiveRecord::StatementInvalid`. *Richard Monette* @@ -62,7 +210,7 @@ *Sean Griffin* -* Fix that unsigned with zerofill is treated as signed. +* Don't treat unsigned integers with zerofill as signed. Fixes #27125. diff --git a/activerecord/MIT-LICENSE b/activerecord/MIT-LICENSE index 1f496cf280..f9e4444f07 100644 --- a/activerecord/MIT-LICENSE +++ b/activerecord/MIT-LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2016 David Heinemeier Hansson +Copyright (c) 2004-2017 David Heinemeier Hansson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/activerecord/Rakefile b/activerecord/Rakefile index ec28df8fea..7be3d851f1 100644 --- a/activerecord/Rakefile +++ b/activerecord/Rakefile @@ -111,11 +111,6 @@ namespace :db do config = ARTest.config["connections"]["postgresql"] %x( createdb -E UTF8 -T template0 #{config["arunit"]["database"]} ) %x( createdb -E UTF8 -T template0 #{config["arunit2"]["database"]} ) - - # prepare hstore - if %x( createdb --version ).strip.gsub(/(.*)(\d\.\d\.\d)$/, "\\2") < "9.1.0" - puts "Please prepare hstore data type. See http://www.postgresql.org/docs/current/static/hstore.html" - end end desc "Drop the PostgreSQL test databases" diff --git a/activerecord/activerecord.gemspec b/activerecord/activerecord.gemspec index 2cd8f179dd..0b37e9076c 100644 --- a/activerecord/activerecord.gemspec +++ b/activerecord/activerecord.gemspec @@ -24,5 +24,5 @@ Gem::Specification.new do |s| s.add_dependency "activesupport", version s.add_dependency "activemodel", version - s.add_dependency "arel", "~> 7.0" + s.add_dependency "arel", "~> 8.0" end diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index 3b5dab16cb..250f48fad9 100644 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -1,5 +1,5 @@ #-- -# Copyright (c) 2004-2016 David Heinemeier Hansson +# Copyright (c) 2004-2017 David Heinemeier Hansson # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the diff --git a/activerecord/lib/active_record/aggregations.rb b/activerecord/lib/active_record/aggregations.rb index 08dfc3a64f..10cbd5429c 100644 --- a/activerecord/lib/active_record/aggregations.rb +++ b/activerecord/lib/active_record/aggregations.rb @@ -15,11 +15,11 @@ module ActiveRecord private - def clear_aggregation_cache # :nodoc: + def clear_aggregation_cache @aggregation_cache.clear if persisted? end - def init_internals # :nodoc: + def init_internals @aggregation_cache = {} super end diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 19308643f3..4606c91ffd 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -97,6 +97,16 @@ module ActiveRecord end end + class HasManyThroughOrderError < ActiveRecordError #:nodoc: + def initialize(owner_class_name = nil, reflection = nil, through_reflection = nil) + if owner_class_name && reflection && through_reflection + super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' which goes through '#{owner_class_name}##{through_reflection.name}' before the through association is defined.") + else + super("Cannot have a has_many :through association before the through association is defined.") + end + end + end + class ThroughCantAssociateThroughHasOneOrManyReflection < ActiveRecordError #:nodoc: def initialize(owner = nil, reflection = nil) if owner && reflection @@ -107,6 +117,21 @@ module ActiveRecord end end + class AmbiguousSourceReflectionForThroughAssociation < ActiveRecordError # :nodoc: + def initialize(klass, macro, association_name, options, possible_sources) + example_options = options.dup + example_options[:source] = possible_sources.first + + super("Ambiguous source reflection for through association. Please " \ + "specify a :source directive on your declaration like:\n" \ + "\n" \ + " class #{klass} < ActiveRecord::Base\n" \ + " #{macro} :#{association_name}, #{example_options}\n" \ + " end" + ) + end + end + class HasManyThroughCantAssociateThroughHasOneOrManyReflection < ThroughCantAssociateThroughHasOneOrManyReflection #:nodoc: end @@ -260,11 +285,11 @@ module ActiveRecord private # Clears out the association cache. - def clear_association_cache # :nodoc: + def clear_association_cache @association_cache.clear if persisted? end - def init_internals # :nodoc: + def init_internals @association_cache = {} super end @@ -354,23 +379,23 @@ module ActiveRecord # # === Overriding generated methods # - # Association methods are generated in a module that is included into the model class, - # which allows you to easily override with your own methods and call the original - # generated method with +super+. For example: + # Association methods are generated in a module included into the model + # class, making overrides easy. The original generated method can thus be + # called with +super+: # # class Car < ActiveRecord::Base # belongs_to :owner # belongs_to :old_owner + # # def owner=(new_owner) # self.old_owner = self.owner # super # end # end # - # If your model class is <tt>Project</tt>, then the module is - # named <tt>Project::GeneratedAssociationMethods</tt>. The +GeneratedAssociationMethods+ module is - # included in the model class immediately after the (anonymous) generated attributes methods - # module, meaning an association will override the methods for an attribute with the same name. + # The association methods module is included immediately after the + # generated attributes methods module, meaning an association will + # override the methods for an attribute with the same name. # # == Cardinality and associations # @@ -1802,7 +1827,7 @@ module ActiveRecord builder = Builder::HasAndBelongsToMany.new name, self, options - join_model = builder.through_model + join_model = ActiveSupport::Deprecation.silence { builder.through_model } const_set join_model.name, join_model private_constant join_model.name @@ -1831,8 +1856,8 @@ module ActiveRecord hm_options[k] = options[k] if options.key? k end - has_many name, scope, hm_options, &extension - self._reflections[name.to_s].parent_reflection = habtm_reflection + ActiveSupport::Deprecation.silence { has_many name, scope, hm_options, &extension } + _reflections[name.to_s].parent_reflection = habtm_reflection end end end diff --git a/activerecord/lib/active_record/associations/association_scope.rb b/activerecord/lib/active_record/associations/association_scope.rb index 12f8c1ccd4..c6d204d3c2 100644 --- a/activerecord/lib/active_record/associations/association_scope.rb +++ b/activerecord/lib/active_record/associations/association_scope.rb @@ -49,6 +49,8 @@ module ActiveRecord binds end + # TODO Change this to private once we've dropped Ruby 2.2 support. + # Workaround for Ruby 2.2 "private attribute?" warning. protected attr_reader :value_transformation diff --git a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb index 42a90b449c..6b71826431 100644 --- a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb +++ b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb @@ -78,9 +78,9 @@ module ActiveRecord::Associations::Builder # :nodoc: private - def self.suppress_composite_primary_key(pk) - pk unless pk.is_a?(Array) - end + def self.suppress_composite_primary_key(pk) + pk unless pk.is_a?(Array) + end } join_model.name = "HABTM_#{association_name.to_s.camelize}" diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb index 46923f690a..0437a79b84 100644 --- a/activerecord/lib/active_record/associations/collection_association.rb +++ b/activerecord/lib/active_record/associations/collection_association.rb @@ -25,16 +25,8 @@ module ActiveRecord # +load_target+ and the +loaded+ flag are your friends. class CollectionAssociation < Association #:nodoc: # Implements the reader method, e.g. foo.items for Foo.has_many :items - def reader(force_reload = false) - if force_reload - ActiveSupport::Deprecation.warn(<<-MSG.squish) - Passing an argument to force an association to reload is now - deprecated and will be removed in Rails 5.1. Please call `reload` - on the result collection proxy instead. - MSG - - klass.uncached { reload } - elsif stale_target? + def reader + if stale_target? reload end @@ -55,9 +47,7 @@ module ActiveRecord # Implements the ids reader method, e.g. foo.item_ids for Foo.has_many :items def ids_reader if loaded? - load_target.map do |record| - record.send(reflection.association_primary_key) - end + target.pluck(reflection.association_primary_key) else @association_ids ||= ( column = "#{reflection.quoted_table_name}.#{reflection.association_primary_key}" @@ -299,16 +289,28 @@ module ActiveRecord def replace_on_target(record, index, skip_callbacks) callback(:before_add, record) unless skip_callbacks - yield(record) if block_given? + begin + if index + record_was = target[index] + target[index] = record + else + target << record + end - if index - @target[index] = record - else - append_record(record) + set_inverse_instance(record) + + yield(record) if block_given? + rescue + if index + target[index] = record_was + else + target.delete(record) + end + + raise end callback(:after_add, record) unless skip_callbacks - set_inverse_instance(record) record end @@ -502,10 +504,6 @@ module ActiveRecord load_target.select { |r| ids.include?(r.id.to_s) } end end - - def append_record(record) - @target << record unless @target.include?(record) - end end end end diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index 35a98d7090..0d84805b4d 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -1126,7 +1126,7 @@ module ActiveRecord self end - protected + private def find_nth_with_limit(index, limit) load_target if find_from_target? @@ -1138,8 +1138,6 @@ module ActiveRecord super end - private - def null_scope? @association.null_scope? end diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb index 742cd25509..b413eb3f9c 100644 --- a/activerecord/lib/active_record/associations/has_many_association.rb +++ b/activerecord/lib/active_record/associations/has_many_association.rb @@ -16,14 +16,7 @@ module ActiveRecord when :restrict_with_error unless empty? record = owner.class.human_attribute_name(reflection.name).downcase - message = owner.errors.generate_message(:base, :'restrict_dependent_destroy.many', record: record, raise: true) rescue nil - if message - ActiveSupport::Deprecation.warn(<<-MESSAGE.squish) - The error key `:'restrict_dependent_destroy.many'` has been deprecated and will be removed in Rails 5.1. - Please use `:'restrict_dependent_destroy.has_many'` instead. - MESSAGE - end - owner.errors.add(:base, message || :'restrict_dependent_destroy.has_many', record: record) + owner.errors.add(:base, :'restrict_dependent_destroy.has_many', record: record) throw(:abort) end @@ -38,7 +31,6 @@ module ActiveRecord def insert_record(record, validate = true, raise = false) set_owner_attributes(record) - set_inverse_instance(record) if raise record.save!(validate: validate) @@ -108,7 +100,7 @@ module ActiveRecord end def delete_or_nullify_all_records(method) - count = delete_count(method, self.scope) + count = delete_count(method, scope) update_counter(-count) end diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb index 8c90aea975..c4a7fe4432 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -38,10 +38,12 @@ module ActiveRecord def insert_record(record, validate = true, raise = false) ensure_not_nested - if raise - record.save!(validate: validate) - else - return unless record.save(validate: validate) + if record.new_record? || record.has_changes_to_save? + if raise + record.save!(validate: validate) + else + return unless record.save(validate: validate) + end end save_through_record(record) @@ -206,10 +208,6 @@ module ActiveRecord def invertible_for?(record) false end - - def append_record(record) - @target << record - end end end end diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb index 21bd668dff..b624154def 100644 --- a/activerecord/lib/active_record/associations/has_one_association.rb +++ b/activerecord/lib/active_record/associations/has_one_association.rb @@ -12,14 +12,7 @@ module ActiveRecord when :restrict_with_error if load_target record = owner.class.human_attribute_name(reflection.name).downcase - message = owner.errors.generate_message(:base, :'restrict_dependent_destroy.one', record: record, raise: true) rescue nil - if message - ActiveSupport::Deprecation.warn(<<-MESSAGE.squish) - The error key `:'restrict_dependent_destroy.one'` has been deprecated and will be removed in Rails 5.1. - Please use `:'restrict_dependent_destroy.has_one'` instead. - MESSAGE - end - owner.errors.add(:base, message || :'restrict_dependent_destroy.has_one', record: record) + owner.errors.add(:base, :'restrict_dependent_destroy.has_one', record: record) throw(:abort) end diff --git a/activerecord/lib/active_record/associations/singular_association.rb b/activerecord/lib/active_record/associations/singular_association.rb index ee7b7c8bea..91580a28d0 100644 --- a/activerecord/lib/active_record/associations/singular_association.rb +++ b/activerecord/lib/active_record/associations/singular_association.rb @@ -2,16 +2,8 @@ module ActiveRecord module Associations class SingularAssociation < Association #:nodoc: # Implements the reader method, e.g. foo.bar for Foo.has_one :bar - def reader(force_reload = false) - if force_reload && klass - ActiveSupport::Deprecation.warn(<<-MSG.squish) - Passing an argument to force an association to reload is now - deprecated and will be removed in Rails 5.1. Please call `reload` - on the parent object instead. - MSG - - klass.uncached { reload } - elsif !loaded? || stale_target? + def reader + if !loaded? || stale_target? reload end diff --git a/activerecord/lib/active_record/associations/through_association.rb b/activerecord/lib/active_record/associations/through_association.rb index f4129edc5a..6b87993ba3 100644 --- a/activerecord/lib/active_record/associations/through_association.rb +++ b/activerecord/lib/active_record/associations/through_association.rb @@ -4,7 +4,7 @@ module ActiveRecord module ThroughAssociation #:nodoc: delegate :source_reflection, :through_reflection, to: :reflection - protected + private # We merge in these scopes for two reasons: # @@ -21,8 +21,6 @@ module ActiveRecord scope end - private - # Construct attributes for :through pointing to owner and associate. This is used by the # methods which create and delete records on the association. # diff --git a/activerecord/lib/active_record/attribute.rb b/activerecord/lib/active_record/attribute.rb index 0b08c2a39b..38281158d8 100644 --- a/activerecord/lib/active_record/attribute.rb +++ b/activerecord/lib/active_record/attribute.rb @@ -128,11 +128,22 @@ module ActiveRecord coder["value"] = value if defined?(@value) end + # TODO Change this to private once we've dropped Ruby 2.2 support. + # Workaround for Ruby 2.2 "private attribute?" warning. protected attr_reader :original_attribute alias_method :assigned?, :original_attribute + def original_value_for_database + if assigned? + original_attribute.original_value_for_database + else + _original_value_for_database + end + end + + private def initialize_dup(other) if defined?(@value) && @value.duplicable? @value = @value.dup @@ -143,14 +154,6 @@ module ActiveRecord assigned? && type.changed?(original_value, value, value_before_type_cast) end - def original_value_for_database - if assigned? - original_attribute.original_value_for_database - else - _original_value_for_database - end - end - def _original_value_for_database type.serialize(original_value) end diff --git a/activerecord/lib/active_record/attribute/user_provided_default.rb b/activerecord/lib/active_record/attribute/user_provided_default.rb index a4e2c2ec85..57f8bbed76 100644 --- a/activerecord/lib/active_record/attribute/user_provided_default.rb +++ b/activerecord/lib/active_record/attribute/user_provided_default.rb @@ -20,6 +20,8 @@ module ActiveRecord self.class.new(name, user_provided_value, type, original_attribute) end + # TODO Change this to private once we've dropped Ruby 2.2 support. + # Workaround for Ruby 2.2 "private attribute?" warning. protected attr_reader :user_provided_value diff --git a/activerecord/lib/active_record/attribute_assignment.rb b/activerecord/lib/active_record/attribute_assignment.rb index 9843e0ca66..d0dfca0cac 100644 --- a/activerecord/lib/active_record/attribute_assignment.rb +++ b/activerecord/lib/active_record/attribute_assignment.rb @@ -12,7 +12,7 @@ module ActiveRecord private - def _assign_attributes(attributes) # :nodoc: + def _assign_attributes(attributes) multi_parameter_attributes = {} nested_parameter_attributes = {} diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index 1ed1deec55..ebe06566cc 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -394,28 +394,21 @@ module ActiveRecord protected - def clone_attribute_value(reader_method, attribute_name) # :nodoc: - value = send(reader_method, attribute_name) - value.duplicable? ? value.clone : value - rescue TypeError, NoMethodError - value + def attribute_method?(attr_name) # :nodoc: + # We check defined? because Syck calls respond_to? before actually calling initialize. + defined?(@attributes) && @attributes.key?(attr_name) end - def arel_attributes_with_values_for_create(attribute_names) # :nodoc: + private + + def arel_attributes_with_values_for_create(attribute_names) arel_attributes_with_values(attributes_for_create(attribute_names)) end - def arel_attributes_with_values_for_update(attribute_names) # :nodoc: + def arel_attributes_with_values_for_update(attribute_names) arel_attributes_with_values(attributes_for_update(attribute_names)) end - def attribute_method?(attr_name) # :nodoc: - # We check defined? because Syck calls respond_to? before actually calling initialize. - defined?(@attributes) && @attributes.key?(attr_name) - end - - private - # Returns a Hash of the Arel::Attributes and attribute values that have been # typecasted for use in an Arel insert/update method. def arel_attributes_with_values(attribute_names) diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb index e20b65e43c..b0e1391cb9 100644 --- a/activerecord/lib/active_record/attribute_methods/dirty.rb +++ b/activerecord/lib/active_record/attribute_methods/dirty.rb @@ -226,6 +226,11 @@ module ActiveRecord super end + def changed?(*) + emit_warning_if_needed("changed?", "saved_changes?") + super + end + def changed(*) emit_warning_if_needed("changed", "saved_changes.keys") super diff --git a/activerecord/lib/active_record/attribute_methods/primary_key.rb b/activerecord/lib/active_record/attribute_methods/primary_key.rb index 287367f92a..2f32caa257 100644 --- a/activerecord/lib/active_record/attribute_methods/primary_key.rb +++ b/activerecord/lib/active_record/attribute_methods/primary_key.rb @@ -9,7 +9,7 @@ module ActiveRecord # available. def to_key sync_with_transaction_state - key = self.id + key = id [key] if key end @@ -50,7 +50,7 @@ module ActiveRecord attribute_in_database(self.class.primary_key) end - protected + private def attribute_method?(attr_name) attr_name == "id" || super diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb index 5448ebc165..369a6e35aa 100644 --- a/activerecord/lib/active_record/attribute_methods/read.rb +++ b/activerecord/lib/active_record/attribute_methods/read.rb @@ -4,7 +4,7 @@ module ActiveRecord extend ActiveSupport::Concern module ClassMethods - protected + private # We want to generate the methods via module_eval rather than # define_method, because define_method is slower on dispatch. 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 500d903857..df1231ad47 100644 --- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb +++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb @@ -63,7 +63,7 @@ module ActiveRecord self.skip_time_zone_conversion_for_attributes = [] class_attribute :time_zone_aware_types, instance_writer: false - self.time_zone_aware_types = [:datetime, :not_explicitly_configured] + self.time_zone_aware_types = [:datetime, :time] end module ClassMethods @@ -86,29 +86,8 @@ module ActiveRecord def create_time_zone_conversion_attribute?(name, cast_type) enabled_for_column = time_zone_aware_attributes && !skip_time_zone_conversion_for_attributes.include?(name.to_sym) - result = enabled_for_column && - time_zone_aware_types.include?(cast_type.type) - if enabled_for_column && - !result && - cast_type.type == :time && - time_zone_aware_types.include?(:not_explicitly_configured) - ActiveSupport::Deprecation.warn(<<-MESSAGE.strip_heredoc) - Time columns will become time zone aware in Rails 5.1. This - still causes `String`s to be parsed as if they were in `Time.zone`, - and `Time`s to be converted to `Time.zone`. - - To keep the old behavior, you must add the following to your initializer: - - config.active_record.time_zone_aware_types = [:datetime] - - To silence this deprecation warning, add the following: - - config.active_record.time_zone_aware_types = [:datetime, :time] - MESSAGE - end - - result + enabled_for_column && time_zone_aware_types.include?(cast_type.type) end end end diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb index 0022d526a4..fe0e01db28 100644 --- a/activerecord/lib/active_record/attribute_methods/write.rb +++ b/activerecord/lib/active_record/attribute_methods/write.rb @@ -8,7 +8,7 @@ module ActiveRecord end module ClassMethods - protected + private def define_method_attribute=(name) safe_name = name.unpack("h*".freeze).first diff --git a/activerecord/lib/active_record/attribute_mutation_tracker.rb b/activerecord/lib/active_record/attribute_mutation_tracker.rb index db86b2b294..3417090830 100644 --- a/activerecord/lib/active_record/attribute_mutation_tracker.rb +++ b/activerecord/lib/active_record/attribute_mutation_tracker.rb @@ -60,6 +60,8 @@ module ActiveRecord forced_changes << attr_name.to_s end + # TODO Change this to private once we've dropped Ruby 2.2 support. + # Workaround for Ruby 2.2 "private attribute?" warning. protected attr_reader :attributes, :forced_changes diff --git a/activerecord/lib/active_record/attribute_set.rb b/activerecord/lib/active_record/attribute_set.rb index 5bde1f107c..66b278219a 100644 --- a/activerecord/lib/active_record/attribute_set.rb +++ b/activerecord/lib/active_record/attribute_set.rb @@ -98,6 +98,8 @@ module ActiveRecord attributes == other.attributes end + # TODO Change this to private once we've dropped Ruby 2.2 support. + # Workaround for Ruby 2.2 "private attribute?" warning. protected attr_reader :attributes diff --git a/activerecord/lib/active_record/attribute_set/builder.rb b/activerecord/lib/active_record/attribute_set/builder.rb index 661f996e1a..2f624d32af 100644 --- a/activerecord/lib/active_record/attribute_set/builder.rb +++ b/activerecord/lib/active_record/attribute_set/builder.rb @@ -90,6 +90,8 @@ module ActiveRecord @materialized = true end + # TODO Change this to private once we've dropped Ruby 2.2 support. + # Workaround for Ruby 2.2 "private attribute?" warning. protected attr_reader :types, :values, :additional_types, :delegate_hash, :default diff --git a/activerecord/lib/active_record/attribute_set/yaml_encoder.rb b/activerecord/lib/active_record/attribute_set/yaml_encoder.rb index c86cfc4263..899de14792 100644 --- a/activerecord/lib/active_record/attribute_set/yaml_encoder.rb +++ b/activerecord/lib/active_record/attribute_set/yaml_encoder.rb @@ -31,6 +31,8 @@ module ActiveRecord end end + # TODO Change this to private once we've dropped Ruby 2.2 support. + # Workaround for Ruby 2.2 "private attribute?" warning. protected attr_reader :default_types diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index 9d0b501862..6bccbc06cd 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -328,9 +328,9 @@ module ActiveRecord def association_valid?(reflection, record, index = nil) return true if record.destroyed? || (reflection.options[:autosave] && record.marked_for_destruction?) - validation_context = self.validation_context unless [:create, :update].include?(self.validation_context) + context = validation_context unless [:create, :update].include?(validation_context) - unless valid = record.valid?(validation_context) + unless valid = record.valid?(context) if reflection.options[:autosave] indexed_attribute = !index.nil? && (reflection.options[:index_errors] || ActiveRecord::Base.index_nested_attribute_errors) diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb index f2e3912c6e..eb44887e18 100644 --- a/activerecord/lib/active_record/callbacks.rb +++ b/activerecord/lib/active_record/callbacks.rb @@ -225,6 +225,55 @@ module ActiveRecord # # This way, the +before_destroy+ gets executed before the <tt>dependent: :destroy</tt> is called, and the data is still available. # + # Also, there are cases when you want several callbacks of the same type to + # be executed in order. + # + # For example: + # + # class Topic + # has_many :children + # + # after_save :log_children + # after_save :do_something_else + # + # private + # + # def log_chidren + # # Child processing + # end + # + # def do_something_else + # # Something else + # end + # end + # + # In this case the +log_children+ gets executed before +do_something_else+. + # The same applies to all non-transactional callbacks. + # + # In case there are multiple transactional callbacks as seen below, the order + # is reversed. + # + # For example: + # + # class Topic + # has_many :children + # + # after_commit :log_children + # after_commit :do_something_else + # + # private + # + # def log_chidren + # # Child processing + # end + # + # def do_something_else + # # Something else + # end + # end + # + # In this case the +do_something_else+ gets executed before +log_children+. + # # == \Transactions # # The entire callback chain of a {#save}[rdoc-ref:Persistence#save], {#save!}[rdoc-ref:Persistence#save!], @@ -283,15 +332,15 @@ module ActiveRecord private - def create_or_update(*) #:nodoc: + def create_or_update(*) _run_save_callbacks { super } end - def _create_record #:nodoc: + def _create_record _run_create_callbacks { super } end - def _update_record(*) #:nodoc: + def _update_record(*) _run_update_callbacks { super } end end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb index 947796eea0..769f488469 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb @@ -51,8 +51,7 @@ module ActiveRecord # Returns a single value from a record def select_value(arel, name = nil, binds = []) - arel, binds = binds_from_relation arel, binds - if result = select_rows(to_sql(arel, binds), name, binds).first + if result = select_rows(arel, name, binds).first result.first end end @@ -60,14 +59,13 @@ module ActiveRecord # Returns an array of the values of the first column in a select: # select_values("SELECT id FROM companies LIMIT 3") => [1,2,3] def select_values(arel, name = nil, binds = []) - arel, binds = binds_from_relation arel, binds - select_rows(to_sql(arel, binds), name, binds).map(&:first) + select_rows(arel, name, binds).map(&:first) end # Returns an array of arrays containing the field values. # Order is the same as that returned by +columns+. - def select_rows(sql, name = nil, binds = []) - exec_query(sql, name, binds).rows + def select_rows(arel, name = nil, binds = []) + select_all(arel, name, binds).rows end # Executes the SQL statement in the context of this connection and returns @@ -126,22 +124,16 @@ module ActiveRecord id_value || last_inserted_id(value) end alias create insert - alias insert_sql insert - deprecate insert_sql: :insert # Executes the update statement and returns the number of rows affected. def update(arel, name = nil, binds = []) exec_update(to_sql(arel, binds), name, binds) end - alias update_sql update - deprecate update_sql: :update # Executes the delete statement and returns the number of rows affected. def delete(arel, name = nil, binds = []) exec_delete(to_sql(arel, binds), name, binds) end - alias delete_sql delete - deprecate delete_sql: :delete # Returns +true+ when the connection adapter supports prepared statement # caching, otherwise returns +false+ @@ -334,17 +326,12 @@ module ActiveRecord # Sanitizes the given LIMIT parameter in order to prevent SQL injection. # # The +limit+ may be anything that can evaluate to a string via #to_s. It - # should look like an integer, or a comma-delimited list of integers, or - # an Arel SQL literal. + # should look like an integer, or an Arel SQL literal. # # Returns Integer and Arel::Nodes::SqlLiteral limits as is. - # Returns the sanitized limit parameter, either as an integer, or as a - # string which contains a comma-delimited list of integers. def sanitize_limit(limit) if limit.is_a?(Integer) || limit.is_a?(Arel::Nodes::SqlLiteral) limit - elsif limit.to_s.include?(",") - Arel.sql limit.to_s.split(",").map { |i| Integer(i) }.join(",") else Integer(limit) end @@ -360,7 +347,7 @@ module ActiveRecord end alias join_to_delete join_to_update - protected + private # Returns a subquery for the given key using the join information. def subquery_for(key, select) diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb index bbd52b8a91..0c6bc16e6f 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb @@ -5,20 +5,10 @@ module ActiveRecord module Quoting # Quotes the column value to help prevent # {SQL injection attacks}[http://en.wikipedia.org/wiki/SQL_injection]. - def quote(value, column = nil) + def quote(value) # records are quoted as their primary key return value.quoted_id if value.respond_to?(:quoted_id) - if column - ActiveSupport::Deprecation.warn(<<-MSG.squish) - Passing a column to `quote` has been deprecated. It is only used - for type casting, which should be handled elsewhere. See - https://github.com/rails/arel/commit/6160bfbda1d1781c3b08a33ec4955f170e95be11 - for more information. - MSG - value = type_cast_from_column(column, value) - end - _quote(value) 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 f783b1941b..9b324c090b 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -100,6 +100,8 @@ module ActiveRecord end end + # TODO Change this to private once we've dropped Ruby 2.2 support. + # Workaround for Ruby 2.2 "private attribute?" warning. protected attr_reader :name, :polymorphic, :index, :foreign_key, :type, :options diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index 5623257fe8..1bdc086380 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -43,7 +43,7 @@ module ActiveRecord end # Returns an array of table names defined in the database. - def tables(name = nil) + def tables raise NotImplementedError, "#tables is not implemented" end @@ -69,7 +69,9 @@ module ActiveRecord end # Returns an array of indexes for the given table. - # def indexes(table_name, name = nil) end + def indexes(table_name, name = nil) + raise NotImplementedError, "#indexes is not implemented" + end # Checks to see if an index exists on a table for a given index definition. # @@ -994,15 +996,15 @@ module ActiveRecord end def insert_versions_sql(versions) # :nodoc: - sm_table = ActiveRecord::Migrator.schema_migrations_table_name + sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name) if versions.is_a?(Array) sql = "INSERT INTO #{sm_table} (version) VALUES\n" - sql << versions.map { |v| "('#{v}')" }.join(",\n") + sql << versions.map { |v| "(#{quote(v)})" }.join(",\n") sql << ";\n\n" sql else - "INSERT INTO #{sm_table} (version) VALUES ('#{versions}');" + "INSERT INTO #{sm_table} (version) VALUES (#{quote(versions)});" end end @@ -1032,7 +1034,7 @@ module ActiveRecord end unless migrated.include?(version) - execute "INSERT INTO #{sm_table} (version) VALUES ('#{version}')" + execute "INSERT INTO #{sm_table} (version) VALUES (#{quote(version)})" end inserting = (versions - migrated).select { |v| v < version } @@ -1169,7 +1171,7 @@ module ActiveRecord raise NotImplementedError, "#{self.class} does not support changing column comments" end - protected + private def add_index_sort_order(quoted_columns, **options) if order = options[:order] @@ -1253,7 +1255,6 @@ module ActiveRecord end end - private def create_table_definition(*args) TableDefinition.new(*args) end @@ -1262,7 +1263,7 @@ module ActiveRecord AlterTable.new create_table_definition(name) end - def index_name_options(column_names) # :nodoc: + def index_name_options(column_names) if column_names.is_a?(String) column_names = column_names.scan(/\w+/).join("_") end @@ -1270,7 +1271,7 @@ module ActiveRecord { column: column_names } end - def foreign_key_name(table_name, options) # :nodoc: + def foreign_key_name(table_name, options) identifier = "#{table_name}_#{options.fetch(:column)}_fk" hashed_identifier = Digest::SHA256.hexdigest(identifier).first(10) options.fetch(:name) do @@ -1278,7 +1279,7 @@ module ActiveRecord end end - def validate_index_length!(table_name, new_name, internal = false) # :nodoc: + def validate_index_length!(table_name, new_name, internal = false) max_index_length = internal ? index_name_length : allowed_index_name_length if new_name.length > max_index_length diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index 284529b46e..4046b3829d 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -499,9 +499,9 @@ module ActiveRecord result end - protected + private - def initialize_type_map(m) # :nodoc: + def initialize_type_map(m) register_class_with_limit m, %r(boolean)i, Type::Boolean register_class_with_limit m, %r(char)i, Type::String register_class_with_limit m, %r(binary)i, Type::Binary @@ -532,37 +532,37 @@ module ActiveRecord end end - def reload_type_map # :nodoc: + def reload_type_map type_map.clear initialize_type_map(type_map) end - def register_class_with_limit(mapping, key, klass) # :nodoc: + def register_class_with_limit(mapping, key, klass) mapping.register_type(key) do |*args| limit = extract_limit(args.last) klass.new(limit: limit) end end - def register_class_with_precision(mapping, key, klass) # :nodoc: + def register_class_with_precision(mapping, key, klass) mapping.register_type(key) do |*args| precision = extract_precision(args.last) klass.new(precision: precision) end end - def extract_scale(sql_type) # :nodoc: + def extract_scale(sql_type) case sql_type when /\((\d+)\)/ then 0 when /\((\d+)(,(\d+))\)/ then $3.to_i end end - def extract_precision(sql_type) # :nodoc: + def extract_precision(sql_type) $1.to_i if sql_type =~ /\((\d+)(,\d+)?\)/ end - def extract_limit(sql_type) # :nodoc: + def extract_limit(sql_type) case sql_type when /^bigint/i 8 @@ -583,7 +583,7 @@ module ActiveRecord exception end - def log(sql, name = "SQL", binds = [], type_casted_binds = [], statement_name = nil) + def log(sql, name = "SQL", binds = [], type_casted_binds = [], statement_name = nil) # :doc: @instrumenter.instrument( "sql.active_record", sql: sql, @@ -610,7 +610,7 @@ module ActiveRecord !prepared_statements || binds.empty? end - def column_for(table_name, column_name) # :nodoc: + def column_for(table_name, column_name) column_name = column_name.to_s columns(table_name).detect { |c| c.name == column_name } || raise(ActiveRecordError, "No such column: #{table_name}.#{column_name}") 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 6985d2c1b2..aedf4581f5 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -67,8 +67,8 @@ module ActiveRecord @statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit])) - if version < "5.0.0" - raise "Your version of MySQL (#{full_version.match(/^\d+\.\d+\.\d+/)[0]}) is too old. Active Record supports MySQL >= 5.0." + if version < "5.1.10" + raise "Your version of MySQL (#{full_version.match(/^\d+\.\d+\.\d+/)[0]}) is too old. Active Record supports MySQL >= 5.1.10." end end @@ -310,45 +310,36 @@ module ActiveRecord show_variable "collation_database" end - def tables(name = nil) # :nodoc: - ActiveSupport::Deprecation.warn(<<-MSG.squish) - #tables currently returns both tables and views. - This behavior is deprecated and will be changed with Rails 5.1 to only return tables. - Use #data_sources instead. - MSG + def tables # :nodoc: + sql = "SELECT table_name FROM information_schema.tables WHERE table_type = 'BASE TABLE'" + sql << " AND table_schema = #{quote(@config[:database])}" - if name - ActiveSupport::Deprecation.warn(<<-MSG.squish) - Passing arguments to #tables is deprecated without replacement. - MSG - end + select_values(sql, "SCHEMA") + end - data_sources + def views # :nodoc: + select_values("SHOW FULL TABLES WHERE table_type = 'VIEW'", "SCHEMA") end - def data_sources + def data_sources # :nodoc: sql = "SELECT table_name FROM information_schema.tables " sql << "WHERE table_schema = #{quote(@config[:database])}" select_values(sql, "SCHEMA") end - def truncate(table_name, name = nil) - execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name - end + def table_exists?(table_name) # :nodoc: + return false unless table_name.present? - def table_exists?(table_name) - # Update lib/active_record/internal_metadata.rb when this gets removed - ActiveSupport::Deprecation.warn(<<-MSG.squish) - #table_exists? currently checks both tables and views. - This behavior is deprecated and will be changed with Rails 5.1 to only check tables. - Use #data_source_exists? instead. - MSG + schema, name = extract_schema_qualified_name(table_name) + + sql = "SELECT table_name FROM information_schema.tables WHERE table_type = 'BASE TABLE'" + sql << " AND table_schema = #{quote(schema)} AND table_name = #{quote(name)}" - data_source_exists?(table_name) + select_values(sql, "SCHEMA").any? end - def data_source_exists?(table_name) + def data_source_exists?(table_name) # :nodoc: return false unless table_name.present? schema, name = extract_schema_qualified_name(table_name) @@ -359,10 +350,6 @@ module ActiveRecord select_values(sql, "SCHEMA").any? end - def views # :nodoc: - select_values("SHOW FULL TABLES WHERE table_type = 'VIEW'", "SCHEMA") - end - def view_exists?(view_name) # :nodoc: return false unless view_name.present? @@ -374,8 +361,18 @@ module ActiveRecord select_values(sql, "SCHEMA").any? end + def truncate(table_name, name = nil) + execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name + end + # Returns an array of indexes for the given table. def indexes(table_name, name = nil) #:nodoc: + if name + ActiveSupport::Deprecation.warn(<<-MSG.squish) + Passing name to #indexes is deprecated without replacement. + MSG + end + indexes = [] current_index = nil execute_and_free("SHOW KEYS FROM #{quote_table_name(table_name)}", "SCHEMA") do |result| @@ -530,6 +527,7 @@ module ActiveRecord WHERE fk.referenced_column_name IS NOT NULL AND fk.table_schema = #{quote(schema)} AND fk.table_name = #{quote(name)} + AND rc.table_name = #{quote(name)} SQL fk_info.map do |row| @@ -649,9 +647,9 @@ module ActiveRecord !native_database_types[type].nil? end - protected + private - def initialize_type_map(m) # :nodoc: + def initialize_type_map(m) super register_class_with_limit m, %r(char)i, MysqlString @@ -691,7 +689,7 @@ module ActiveRecord end end - def register_integer_type(mapping, key, options) # :nodoc: + def register_integer_type(mapping, key, options) mapping.register_type(key) do |sql_type| if /\bunsigned\b/.match?(sql_type) Type::UnsignedInteger.new(options) @@ -837,8 +835,6 @@ module ActiveRecord [remove_column_sql(table_name, :updated_at), remove_column_sql(table_name, :created_at)] end - private - # MySQL is too stupid to create a temporary table for use subquery, so we have # to give it some prompting in the form of a subsubquery. Ugh! def subquery_for(key, select) @@ -908,7 +904,7 @@ module ActiveRecord end.compact.join(", ") # ...and send them all in one query - @connection.query "SET #{encoding} #{sql_mode_assignment} #{variable_assignments}" + execute "SET #{encoding} #{sql_mode_assignment} #{variable_assignments}" end def column_definitions(table_name) # :nodoc: diff --git a/activerecord/lib/active_record/connection_adapters/mysql/column.rb b/activerecord/lib/active_record/connection_adapters/mysql/column.rb index c66d543752..1499c1681f 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql/column.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql/column.rb @@ -5,8 +5,7 @@ module ActiveRecord delegate :extra, to: :sql_type_metadata, allow_nil: true def unsigned? - # enum and set types do not allow being defined as unsigned. - !/\A(?:enum|set)\b/.match?(sql_type) && /\bunsigned\b/.match?(sql_type) + /\bunsigned(?: zerofill)?\z/.match?(sql_type) end def case_sensitive? diff --git a/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb b/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb index c7098105a8..8c67a7a80b 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb @@ -3,7 +3,7 @@ module ActiveRecord module MySQL module DatabaseStatements # Returns an ActiveRecord::Result instance. - def select_all(arel, name = nil, binds = [], preparable: nil) + def select_all(arel, name = nil, binds = [], preparable: nil) # :nodoc: result = if ExplainRegistry.collect? && prepared_statements unprepared_statement { super } else @@ -15,8 +15,8 @@ module ActiveRecord # Returns an array of arrays containing the field values. # Order is the same as that returned by +columns+. - def select_rows(sql, name = nil, binds = []) - select_result(sql, name, binds) do |result| + def select_rows(arel, name = nil, binds = []) # :nodoc: + select_result(arel, name, binds) do |result| @connection.next_result while @connection.more_results? result.to_a end @@ -52,15 +52,15 @@ module ActiveRecord end alias :exec_update :exec_delete - protected + private def last_inserted_id(result) @connection.last_id end - private - - def select_result(sql, name = nil, binds = []) + def select_result(arel, name, binds) + arel, binds = binds_from_relation(arel, binds) + sql = to_sql(arel, binds) if without_prepared_statement?(binds) execute_and_free(sql, name) { |result| yield result } else 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 520a50506f..705e6063dc 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb @@ -7,18 +7,14 @@ module ActiveRecord PostgreSQL::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", binds)) end - def select_value(arel, name = nil, binds = []) - arel, binds = binds_from_relation arel, binds - sql = to_sql(arel, binds) - execute_and_clear(sql, name, binds) do |result| + def select_value(arel, name = nil, binds = []) # :nodoc: + select_result(arel, name, binds) do |result| result.getvalue(0, 0) if result.ntuples > 0 && result.nfields > 0 end end - def select_values(arel, name = nil, binds = []) - arel, binds = binds_from_relation arel, binds - sql = to_sql(arel, binds) - execute_and_clear(sql, name, binds) do |result| + def select_values(arel, name = nil, binds = []) # :nodoc: + select_result(arel, name, binds) do |result| if result.nfields > 0 result.column_values(0) else @@ -29,8 +25,8 @@ module ActiveRecord # Executes a SELECT query and returns an array of rows. Each row is an # array of field values. - def select_rows(sql, name = nil, binds = []) - execute_and_clear(sql, name, binds) do |result| + def select_rows(arel, name = nil, binds = []) # :nodoc: + select_result(arel, name, binds) do |result| result.values end end @@ -134,7 +130,7 @@ module ActiveRecord super end - protected :sql_for_insert + private :sql_for_insert def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil) if use_insert_returning? || pk == false @@ -179,6 +175,14 @@ module ActiveRecord def suppress_composite_primary_key(pk) pk unless pk.is_a?(Array) end + + def select_result(arel, name, binds) + arel, binds = binds_from_relation(arel, binds) + sql = to_sql(arel, binds) + execute_and_clear(sql, name, binds) do |result| + yield result + end + end end end end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb index d9daaaa23e..e1a75f8e5e 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb @@ -5,8 +5,10 @@ module ActiveRecord class Array < Type::Value # :nodoc: include Type::Helpers::Mutable + Data = Struct.new(:encoder, :values) # :nodoc: + attr_reader :subtype, :delimiter - delegate :type, :user_input_in_time_zone, :limit, to: :subtype + delegate :type, :user_input_in_time_zone, :limit, :precision, :scale, to: :subtype def initialize(subtype, delimiter = ",") @subtype = subtype @@ -17,8 +19,11 @@ module ActiveRecord end def deserialize(value) - if value.is_a?(::String) + case value + when ::String type_cast_array(@pg_decoder.decode(value), :deserialize) + when Data + deserialize(value.values) else super end @@ -33,11 +38,8 @@ module ActiveRecord def serialize(value) if value.is_a?(::Array) - result = @pg_encoder.encode(type_cast_array(value, :serialize)) - if encoding = determine_encoding_of_strings(value) - result.force_encoding(encoding) - end - result + casted_values = type_cast_array(value, :serialize) + Data.new(@pg_encoder, casted_values) else super end @@ -58,6 +60,10 @@ module ActiveRecord value.map(&block) end + def changed_in_place?(raw_old_value, new_value) + deserialize(raw_old_value) != new_value + end + private def type_cast_array(value, method) @@ -67,13 +73,6 @@ module ActiveRecord @subtype.public_send(method, value) end end - - def determine_encoding_of_strings(value) - case value - when ::Array then determine_encoding_of_strings(value.first) - when ::String then value.encoding - end - end end end end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/bit.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/bit.rb index 302d393277..0a505f46a7 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/bit.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/bit.rb @@ -41,6 +41,8 @@ module ActiveRecord /\A[0-9A-F]*\Z/i.match?(value) end + # TODO Change this to private once we've dropped Ruby 2.2 support. + # Workaround for Ruby 2.2 "private attribute?" warning. protected attr_reader :value diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/hstore.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/hstore.rb index d629ebca91..49dd4fc73f 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/hstore.rb @@ -35,6 +35,14 @@ module ActiveRecord ActiveRecord::Store::StringKeyedHashAccessor end + # Will compare the Hash equivalents of +raw_old_value+ and +new_value+. + # By comparing hashes, this avoids an edge case where the order of + # the keys change between the two hashes, and they would not be marked + # as equal. + def changed_in_place?(raw_old_value, new_value) + deserialize(raw_old_value) != new_value + end + private HstorePair = begin diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb index 2c714f4018..54d5d0902e 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb @@ -1,5 +1,3 @@ -require "active_support/core_ext/string/filters" - module ActiveRecord module ConnectionAdapters module PostgreSQL diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb index b5031d890f..3783925954 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb @@ -92,6 +92,8 @@ module ActiveRecord else super end + when OID::Array::Data + _quote(encode_array(value)) else super end @@ -106,10 +108,37 @@ module ActiveRecord { value: value.to_s, format: 1 } when OID::Xml::Data, OID::Bit::Data value.to_s + when OID::Array::Data + encode_array(value) else super end end + + def encode_array(array_data) + encoder = array_data.encoder + values = type_cast_array(array_data.values) + + result = encoder.encode(values) + if encoding = determine_encoding_of_strings_in_array(values) + result.force_encoding(encoding) + end + result + end + + def determine_encoding_of_strings_in_array(value) + case value + when ::Array then determine_encoding_of_strings_in_array(value.first) + when ::String then value.encoding + end + end + + def type_cast_array(values) + case values + when ::Array then values.map { |item| type_cast_array(item) } + else _type_cast(values) + end + end end end end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb index 9e7487b27f..bfda113e40 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb @@ -71,13 +71,7 @@ module ActiveRecord end # Returns the list of all tables in the schema search path. - def tables(name = nil) - if name - ActiveSupport::Deprecation.warn(<<-MSG.squish) - Passing arguments to #tables is deprecated without replacement. - MSG - end - + def tables select_values("SELECT tablename FROM pg_tables WHERE schemaname = ANY(current_schemas(false))", "SCHEMA") end @@ -91,40 +85,42 @@ module ActiveRecord SQL end + def views # :nodoc: + select_values(<<-SQL, "SCHEMA") + SELECT c.relname + FROM pg_class c + LEFT JOIN pg_namespace n ON n.oid = c.relnamespace + WHERE c.relkind IN ('v','m') -- (v)iew, (m)aterialized view + AND n.nspname = ANY (current_schemas(false)) + SQL + end + # Returns true if table exists. # If the schema is not specified as part of +name+ then it will only find tables within # the current schema search path (regardless of permissions to access tables in other schemas) def table_exists?(name) - ActiveSupport::Deprecation.warn(<<-MSG.squish) - #table_exists? currently checks both tables and views. - This behavior is deprecated and will be changed with Rails 5.1 to only check tables. - Use #data_source_exists? instead. - MSG - - data_source_exists?(name) - end - - def data_source_exists?(name) name = Utils.extract_schema_qualified_name(name.to_s) return false unless name.identifier select_values(<<-SQL, "SCHEMA").any? - SELECT c.relname - FROM pg_class c - LEFT JOIN pg_namespace n ON n.oid = c.relnamespace - WHERE c.relkind IN ('r','v','m') -- (r)elation/table, (v)iew, (m)aterialized view - AND c.relname = #{quote(name.identifier)} - AND n.nspname = #{name.schema ? quote(name.schema) : "ANY (current_schemas(false))"} + SELECT tablename + FROM pg_tables + WHERE tablename = #{quote(name.identifier)} + AND schemaname = #{name.schema ? quote(name.schema) : "ANY (current_schemas(false))"} SQL end - def views # :nodoc: - select_values(<<-SQL, "SCHEMA") + def data_source_exists?(name) # :nodoc: + name = Utils.extract_schema_qualified_name(name.to_s) + return false unless name.identifier + + select_values(<<-SQL, "SCHEMA").any? SELECT c.relname FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = c.relnamespace - WHERE c.relkind IN ('v','m') -- (v)iew, (m)aterialized view - AND n.nspname = ANY (current_schemas(false)) + WHERE c.relkind IN ('r','v','m') -- (r)elation/table, (v)iew, (m)aterialized view + AND c.relname = #{quote(name.identifier)} + AND n.nspname = #{name.schema ? quote(name.schema) : "ANY (current_schemas(false))"} SQL end @@ -170,7 +166,13 @@ module ActiveRecord end # Returns an array of indexes for the given table. - def indexes(table_name, name = nil) + def indexes(table_name, name = nil) # :nodoc: + if name + ActiveSupport::Deprecation.warn(<<-MSG.squish) + Passing name to #indexes is deprecated without replacement. + MSG + end + table = Utils.extract_schema_qualified_name(table_name.to_s) result = query(<<-SQL, "SCHEMA") diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/utils.rb b/activerecord/lib/active_record/connection_adapters/postgresql/utils.rb index 1412928ca5..a3f9ce6d64 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/utils.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/utils.rb @@ -35,6 +35,12 @@ module ActiveRecord end protected + + def parts + @parts ||= [@schema, @identifier].compact + end + + private def unquote(part) if part && part.start_with?('"') part[1..-2] @@ -42,10 +48,6 @@ module ActiveRecord part end end - - def parts - @parts ||= [@schema, @identifier].compact - end end module Utils # :nodoc: diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 33cdcf9a76..0ebd907cc0 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -404,7 +404,7 @@ module ActiveRecord @connection.server_version end - protected + private # See http://www.postgresql.org/docs/current/static/errcodes-appendix.html VALUE_LIMIT_VIOLATION = "22001" @@ -438,9 +438,7 @@ module ActiveRecord end end - private - - def get_oid_type(oid, fmod, column_name, sql_type = "") # :nodoc: + def get_oid_type(oid, fmod, column_name, sql_type = "") if !type_map.key?(oid) load_additional_types(type_map, [oid]) end @@ -453,7 +451,7 @@ module ActiveRecord } end - def initialize_type_map(m) # :nodoc: + def initialize_type_map(m) register_class_with_limit m, "int2", Type::Integer register_class_with_limit m, "int4", Type::Integer register_class_with_limit m, "int8", Type::Integer @@ -521,7 +519,7 @@ module ActiveRecord load_additional_types(m) end - def extract_limit(sql_type) # :nodoc: + def extract_limit(sql_type) case sql_type when /^bigint/i, /^int8/i 8 @@ -533,7 +531,7 @@ module ActiveRecord end # Extracts the value from a PostgreSQL column default definition. - def extract_value_from_default(default) # :nodoc: + def extract_value_from_default(default) case default # Quoted types when /\A[\(B]?'(.*)'.*::"?([\w. ]+)"?(?:\[\])?\z/m @@ -559,15 +557,15 @@ module ActiveRecord end end - def extract_default_function(default_value, default) # :nodoc: + def extract_default_function(default_value, default) default if has_default_function?(default_value, default) end - def has_default_function?(default_value, default) # :nodoc: + def has_default_function?(default_value, default) !default_value && (%r{\w+\(.*\)|\(.*\)::\w+} === default) end - def load_additional_types(type_map, oids = nil) # :nodoc: + def load_additional_types(type_map, oids = nil) initializer = OID::TypeMapInitializer.new(type_map) if supports_ranges? @@ -735,7 +733,7 @@ module ActiveRecord end # Returns the current ID of a table's sequence. - def last_insert_id_result(sequence_name) # :nodoc: + def last_insert_id_result(sequence_name) exec_query("SELECT currval('#{sequence_name}')", "SQL") end @@ -757,7 +755,7 @@ module ActiveRecord # Query implementation notes: # - format_type includes the column size constraint, e.g. varchar(50) # - ::regclass is a function that gives the id for a table name - def column_definitions(table_name) # :nodoc: + def column_definitions(table_name) query(<<-end_sql, "SCHEMA") SELECT a.attname, format_type(a.atttypid, a.atttypmod), pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod, @@ -772,12 +770,12 @@ module ActiveRecord end_sql end - def extract_table_ref_from_insert_sql(sql) # :nodoc: + def extract_table_ref_from_insert_sql(sql) sql[/into\s("[A-Za-z0-9_."\[\]\s]+"|[A-Za-z0-9_."\[\]]+)\s*/im] $1.strip if $1 end - def create_table_definition(*args) # :nodoc: + def create_table_definition(*args) PostgreSQL::TableDefinition.new(*args) end diff --git a/activerecord/lib/active_record/connection_adapters/schema_cache.rb b/activerecord/lib/active_record/connection_adapters/schema_cache.rb index 3a319c4029..4d339b0a8c 100644 --- a/activerecord/lib/active_record/connection_adapters/schema_cache.rb +++ b/activerecord/lib/active_record/connection_adapters/schema_cache.rb @@ -48,8 +48,6 @@ module ActiveRecord @data_sources[name] = connection.data_source_exists?(name) end - alias table_exists? data_source_exists? - deprecate table_exists?: "use #data_source_exists? instead" # Add internal cache for table with +table_name+. def add(table_name) @@ -63,8 +61,6 @@ module ActiveRecord def data_sources(name) @data_sources[name] end - alias tables data_sources - deprecate tables: "use #data_sources instead" # Get the columns for a table def columns(table_name) @@ -99,8 +95,6 @@ module ActiveRecord @primary_keys.delete name @data_sources.delete name end - alias clear_table_cache! clear_data_source_cache! - deprecate clear_table_cache!: "use #clear_data_source_cache! instead" def marshal_dump # if we get current version during initialization, it happens stack over flow. diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index a7c4a2cd86..ec44d020c2 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -259,47 +259,34 @@ module ActiveRecord # SCHEMA STATEMENTS ======================================== - def tables(name = nil) # :nodoc: - ActiveSupport::Deprecation.warn(<<-MSG.squish) - #tables currently returns both tables and views. - This behavior is deprecated and will be changed with Rails 5.1 to only return tables. - Use #data_sources instead. - MSG - - if name - ActiveSupport::Deprecation.warn(<<-MSG.squish) - Passing arguments to #tables is deprecated without replacement. - MSG - end - - data_sources + def tables # :nodoc: + select_values("SELECT name FROM sqlite_master WHERE type = 'table' AND name <> 'sqlite_sequence'", "SCHEMA") end - def data_sources + def data_sources # :nodoc: select_values("SELECT name FROM sqlite_master WHERE type IN ('table','view') AND name <> 'sqlite_sequence'", "SCHEMA") end - def table_exists?(table_name) - ActiveSupport::Deprecation.warn(<<-MSG.squish) - #table_exists? currently checks both tables and views. - This behavior is deprecated and will be changed with Rails 5.1 to only check tables. - Use #data_source_exists? instead. - MSG - - data_source_exists?(table_name) + def views # :nodoc: + select_values("SELECT name FROM sqlite_master WHERE type = 'view' AND name <> 'sqlite_sequence'", "SCHEMA") end - def data_source_exists?(table_name) + def table_exists?(table_name) # :nodoc: return false unless table_name.present? - sql = "SELECT name FROM sqlite_master WHERE type IN ('table','view') AND name <> 'sqlite_sequence'" + sql = "SELECT name FROM sqlite_master WHERE type = 'table' AND name <> 'sqlite_sequence'" sql << " AND name = #{quote(table_name)}" select_values(sql, "SCHEMA").any? end - def views # :nodoc: - select_values("SELECT name FROM sqlite_master WHERE type = 'view' AND name <> 'sqlite_sequence'", "SCHEMA") + def data_source_exists?(table_name) # :nodoc: + return false unless table_name.present? + + sql = "SELECT name FROM sqlite_master WHERE type IN ('table','view') AND name <> 'sqlite_sequence'" + sql << " AND name = #{quote(table_name)}" + + select_values(sql, "SCHEMA").any? end def view_exists?(view_name) # :nodoc: @@ -329,6 +316,12 @@ module ActiveRecord # Returns an array of indexes for the given table. def indexes(table_name, name = nil) #:nodoc: + if name + ActiveSupport::Deprecation.warn(<<-MSG.squish) + Passing name to #indexes is deprecated without replacement. + MSG + end + exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", "SCHEMA").map do |row| sql = <<-SQL SELECT sql @@ -431,16 +424,16 @@ module ActiveRecord rename_column_indexes(table_name, column.name, new_column_name) end - protected + private - def table_structure(table_name) # :nodoc: + def table_structure(table_name) structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", "SCHEMA") raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty? table_structure_with_collation(table_name, structure) end alias column_definitions table_structure - def alter_table(table_name, options = {}) #:nodoc: + def alter_table(table_name, options = {}) altered_table_name = "a#{table_name}" caller = lambda { |definition| yield definition if block_given? } @@ -451,12 +444,12 @@ module ActiveRecord end end - def move_table(from, to, options = {}, &block) #:nodoc: + def move_table(from, to, options = {}, &block) copy_table(from, to, options, &block) drop_table(from) end - def copy_table(from, to, options = {}) #:nodoc: + def copy_table(from, to, options = {}) from_primary_key = primary_key(from) options[:id] = false create_table(to, options) do |definition| @@ -482,7 +475,7 @@ module ActiveRecord options[:rename] || {}) end - def copy_table_indexes(from, to, rename = {}) #:nodoc: + def copy_table_indexes(from, to, rename = {}) indexes(from).each do |index| name = index.name if to == "a#{from}" @@ -505,7 +498,7 @@ module ActiveRecord end end - def copy_table_contents(from, to, columns, rename = {}) #:nodoc: + def copy_table_contents(from, to, columns, rename = {}) column_mappings = Hash[columns.map { |name| [name, name] }] rename.each { |a| column_mappings[a.last] = a.first } from_columns = columns(from).collect(&:name) @@ -537,7 +530,6 @@ module ActiveRecord end end - private COLLATE_REGEX = /.*\"(\w+)\".*collate\s+\"(\w+)\".*/i.freeze def table_structure_with_collation(table_name, basic_structure) diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb index 878d87638d..4d30bdf196 100644 --- a/activerecord/lib/active_record/core.rb +++ b/activerecord/lib/active_record/core.rb @@ -171,23 +171,19 @@ module ActiveRecord return super if block_given? || primary_key.nil? || scope_attributes? || - columns_hash.include?(inheritance_column) || - ids.first.kind_of?(Array) + columns_hash.include?(inheritance_column) id = ids.first - if ActiveRecord::Base === id - id = id.id - ActiveSupport::Deprecation.warn(<<-MSG.squish) - You are passing an instance of ActiveRecord::Base to `find`. - Please pass the id of the object by calling `.id`. - MSG - end + + return super if id.kind_of?(Array) || + id.is_a?(ActiveRecord::Base) key = primary_key statement = cached_find_by_statement(key) { |params| where(key => params.bind).limit(1) } + record = statement.execute([id], self, connection).first unless record raise RecordNotFound.new("Couldn't find #{name} with '#{primary_key}'=#{id}", @@ -200,12 +196,12 @@ module ActiveRecord end def find_by(*args) # :nodoc: - return super if scope_attributes? || !(Hash === args.first) || reflect_on_all_aggregations.any? + return super if scope_attributes? || reflect_on_all_aggregations.any? hash = args.first - return super if hash.values.any? { |v| - v.nil? || Array === v || Hash === v || Relation === v + return super if !(Hash === hash) || hash.values.any? { |v| + v.nil? || Array === v || Hash === v || Relation === v || Base === v } # We can't cache Post.find_by(author: david) ...yet @@ -239,7 +235,9 @@ module ActiveRecord def generated_association_methods @generated_association_methods ||= begin mod = const_set(:GeneratedAssociationMethods, Module.new) + private_constant :GeneratedAssociationMethods include mod + mod end end @@ -299,14 +297,14 @@ module ActiveRecord private - def cached_find_by_statement(key, &block) # :nodoc: + def cached_find_by_statement(key, &block) cache = @find_by_statement_cache[connection.prepared_statements] cache[key] || cache.synchronize { cache[key] ||= StatementCache.create(connection, &block) } end - def relation # :nodoc: + def relation relation = Relation.create(self, arel_table, predicate_builder) if finder_needs_type_condition? && !ignore_default_scope? @@ -316,7 +314,7 @@ module ActiveRecord end end - def table_metadata # :nodoc: + def table_metadata TableMetadata.new(self, arel_table) end end @@ -452,7 +450,7 @@ module ActiveRecord # [ Person.find(1), Person.find(2), Person.find(3) ] & [ Person.find(1), Person.find(4) ] # => [ Person.find(1) ] def hash if id - self.class.hash ^ self.id.hash + self.class.hash ^ id.hash else super end @@ -474,7 +472,7 @@ module ActiveRecord # Allows sort on objects def <=>(other_object) if other_object.is_a?(self.class) - self.to_key <=> other_object.to_key + to_key <=> other_object.to_key else super end diff --git a/activerecord/lib/active_record/counter_cache.rb b/activerecord/lib/active_record/counter_cache.rb index e2da512813..93b9371206 100644 --- a/activerecord/lib/active_record/counter_cache.rb +++ b/activerecord/lib/active_record/counter_cache.rb @@ -12,13 +12,21 @@ module ActiveRecord # # * +id+ - The id of the object you wish to reset a counter on. # * +counters+ - One or more association counters to reset. Association name or counter name can be given. + # * <tt>:touch</tt> - Touch timestamp columns when updating. + # Pass +true+ to touch +updated_at+ and/or +updated_on+. Pass a symbol to + # touch that column or an array of symbols to touch just those ones. # # ==== Examples # - # # For Post with id #1 records reset the comments_count + # # For the Post with id #1, reset the comments_count # Post.reset_counters(1, :comments) - def reset_counters(id, *counters) + # + # # Like above, but also touch the +updated_at+ and/or +updated_on+ + # # attributes. + # Post.reset_counters(1, :comments, touch: true) + def reset_counters(id, *counters, touch: nil) object = find(id) + counters.each do |counter_association| has_many_association = _reflect_on_association(counter_association) unless has_many_association @@ -37,10 +45,12 @@ module ActiveRecord reflection = child_class._reflections.values.find { |e| e.belongs_to? && e.foreign_key.to_s == foreign_key && e.options[:counter_cache].present? } counter_name = reflection.counter_cache_column - unscoped.where(primary_key => object.id).update_all( - counter_name => object.send(counter_association).count(:all) - ) + updates = { counter_name.to_sym => object.send(counter_association).count(:all) } + updates.merge!(touch_updates(touch)) if touch + + unscoped.where(primary_key => object.id).update_all(updates) end + return true end @@ -55,6 +65,9 @@ module ActiveRecord # * +id+ - The id of the object you wish to update a counter on or an array of ids. # * +counters+ - A Hash containing the names of the fields # to update as keys and the amount to update the field by as values. + # * <tt>:touch</tt> option - Touch timestamp columns when updating. + # Pass +true+ to touch +updated_at+ and/or +updated_on+. Pass a symbol to + # touch that column or an array of symbols to touch just those ones. # # ==== Examples # @@ -73,13 +86,28 @@ module ActiveRecord # # UPDATE posts # # SET comment_count = COALESCE(comment_count, 0) + 1 # # WHERE id IN (10, 15) + # + # # For the Posts with id of 10 and 15, increment the comment_count by 1 + # # and update the updated_at value for each counter. + # Post.update_counters [10, 15], comment_count: 1, touch: true + # # Executes the following SQL: + # # UPDATE posts + # # SET comment_count = COALESCE(comment_count, 0) + 1, + # # `updated_at` = '2016-10-13T09:59:23-05:00' + # # WHERE id IN (10, 15) def update_counters(id, counters) + touch = counters.delete(:touch) + updates = counters.map do |counter_name, value| operator = value < 0 ? "-" : "+" quoted_column = connection.quote_column_name(counter_name) "#{quoted_column} = COALESCE(#{quoted_column}, 0) #{operator} #{value.abs}" end + if touch + updates << sanitize_sql_for_assignment(touch_updates(touch)) + end + unscoped.where(primary_key => id).update_all updates.join(", ") end @@ -94,13 +122,20 @@ module ActiveRecord # # * +counter_name+ - The name of the field that should be incremented. # * +id+ - The id of the object that should be incremented or an array of ids. + # * <tt>:touch</tt> - Touch timestamp columns when updating. + # Pass +true+ to touch +updated_at+ and/or +updated_on+. Pass a symbol to + # touch that column or an array of symbols to touch just those ones. # # ==== Examples # # # Increment the posts_count column for the record with an id of 5 # DiscussionBoard.increment_counter(:posts_count, 5) - def increment_counter(counter_name, id) - update_counters(id, counter_name => 1) + # + # # Increment the posts_count column for the record with an id of 5 + # # and update the updated_at value. + # DiscussionBoard.increment_counter(:posts_count, 5, touch: true) + def increment_counter(counter_name, id, touch: nil) + update_counters(id, counter_name => 1, touch: touch) end # Decrement a numeric field by one, via a direct SQL update. @@ -112,14 +147,28 @@ module ActiveRecord # # * +counter_name+ - The name of the field that should be decremented. # * +id+ - The id of the object that should be decremented or an array of ids. + # * <tt>:touch</tt> - Touch timestamp columns when updating. + # Pass +true+ to touch +updated_at+ and/or +updated_on+. Pass a symbol to + # touch that column or an array of symbols to touch just those ones. # # ==== Examples # # # Decrement the posts_count column for the record with an id of 5 # DiscussionBoard.decrement_counter(:posts_count, 5) - def decrement_counter(counter_name, id) - update_counters(id, counter_name => -1) + # + # # Decrement the posts_count column for the record with an id of 5 + # # and update the updated_at value. + # DiscussionBoard.decrement_counter(:posts_count, 5, touch: true) + def decrement_counter(counter_name, id, touch: nil) + update_counters(id, counter_name => -1, touch: touch) end + + private + def touch_updates(touch) + touch = timestamp_attributes_for_update_in_model if touch == true + touch_time = current_time_from_proper_timezone + Array(touch).map { |column| [ column, touch_time ] }.to_h + end end private diff --git a/activerecord/lib/active_record/enum.rb b/activerecord/lib/active_record/enum.rb index 0a94ab58dd..0ab03b2ab3 100644 --- a/activerecord/lib/active_record/enum.rb +++ b/activerecord/lib/active_record/enum.rb @@ -140,6 +140,8 @@ module ActiveRecord end end + # TODO Change this to private once we've dropped Ruby 2.2 support. + # Workaround for Ruby 2.2 "private attribute?" warning. protected attr_reader :name, :mapping, :subtype diff --git a/activerecord/lib/active_record/errors.rb b/activerecord/lib/active_record/errors.rb index c812a05101..18fac5af1b 100644 --- a/activerecord/lib/active_record/errors.rb +++ b/activerecord/lib/active_record/errors.rb @@ -95,19 +95,9 @@ module ActiveRecord # # Wraps the underlying database error as +cause+. class StatementInvalid < ActiveRecordError - def initialize(message = nil, original_exception = nil) - if original_exception - ActiveSupport::Deprecation.warn("Passing #original_exception is deprecated and has no effect. " \ - "Exceptions will automatically capture the original exception.", caller) - end - + def initialize(message = nil) super(message || $!.try(:message)) end - - def original_exception - ActiveSupport::Deprecation.warn("#original_exception is deprecated. Use #cause instead.", caller) - cause - end end # Defunct wrapper class kept for compatibility. diff --git a/activerecord/lib/active_record/fixture_set/file.rb b/activerecord/lib/active_record/fixture_set/file.rb index 5ba354d758..6cf2e01179 100644 --- a/activerecord/lib/active_record/fixture_set/file.rb +++ b/activerecord/lib/active_record/fixture_set/file.rb @@ -66,10 +66,13 @@ module ActiveRecord # Validate our unmarshalled data. def validate(data) unless Hash === data || YAML::Omap === data - raise Fixture::FormatError, "fixture is not a hash" + raise Fixture::FormatError, "fixture is not a hash: #{@file}" end - raise Fixture::FormatError unless data.all? { |name, row| Hash === row } + invalid = data.reject { |_, row| Hash === row } + if invalid.any? + raise Fixture::FormatError, "fixture key is not a hash: #{@file}, keys: #{invalid.keys.inspect}" + end data end end diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index 21c5e5b5bb..91d8054ef2 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -536,16 +536,16 @@ module ActiveRecord update_all_loaded_fixtures fixtures_map connection.transaction(requires_new: true) do - deleted_tables = Set.new + deleted_tables = Hash.new { |h, k| h[k] = Set.new } fixture_sets.each do |fs| conn = fs.model_class.respond_to?(:connection) ? fs.model_class.connection : connection table_rows = fs.table_rows table_rows.each_key do |table| - unless deleted_tables.include? table + unless deleted_tables[conn].include? table conn.delete "DELETE FROM #{conn.quote_table_name(table)}", "Fixture Delete" end - deleted_tables << table + deleted_tables[conn] << table end table_rows.each do |fixture_set_name, rows| @@ -862,29 +862,17 @@ module ActiveRecord class_attribute :fixture_table_names class_attribute :fixture_class_names class_attribute :use_transactional_tests - class_attribute :use_transactional_fixtures class_attribute :use_instantiated_fixtures # true, false, or :no_instances class_attribute :pre_loaded_fixtures class_attribute :config - singleton_class.deprecate "use_transactional_fixtures=" => "use use_transactional_tests= instead" - self.fixture_table_names = [] self.use_instantiated_fixtures = false self.pre_loaded_fixtures = false self.config = ActiveRecord::Base self.fixture_class_names = {} - - silence_warnings do - define_singleton_method :use_transactional_tests do - if use_transactional_fixtures.nil? - true - else - use_transactional_fixtures - end - end - end + self.use_transactional_tests = true end module ClassMethods @@ -897,7 +885,7 @@ module ActiveRecord # # The keys must be the fixture names, that coincide with the short paths to the fixture files. def set_fixture_class(class_names = {}) - self.fixture_class_names = self.fixture_class_names.merge(class_names.stringify_keys) + self.fixture_class_names = fixture_class_names.merge(class_names.stringify_keys) end def fixtures(*fixture_set_names) diff --git a/activerecord/lib/active_record/inheritance.rb b/activerecord/lib/active_record/inheritance.rb index a1d4f47372..fbdaeaae51 100644 --- a/activerecord/lib/active_record/inheritance.rb +++ b/activerecord/lib/active_record/inheritance.rb @@ -130,16 +130,26 @@ module ActiveRecord store_full_sti_class ? name : name.demodulize end + def inherited(subclass) + subclass.instance_variable_set(:@_type_candidates_cache, Concurrent::Map.new) + super + end + protected # Returns the class type of the record using the current module as a prefix. So descendants of # MyApp::Business::Account would appear as MyApp::Business::AccountSubclass. def compute_type(type_name) - if type_name.match(/^::/) + if type_name.start_with?("::".freeze) # If the type is prefixed with a scope operator then we assume that # the type_name is an absolute reference. ActiveSupport::Dependencies.constantize(type_name) else + type_candidate = @_type_candidates_cache[type_name] + if type_candidate && type_constant = ActiveSupport::Dependencies.safe_constantize(type_candidate) + return type_constant + end + # Build a list of candidates to search for candidates = [] name.scan(/::|$/) { candidates.unshift "#{$`}::#{type_name}" } @@ -147,7 +157,10 @@ module ActiveRecord candidates.each do |candidate| constant = ActiveSupport::Dependencies.safe_constantize(candidate) - return constant if candidate == constant.to_s + if candidate == constant.to_s + @_type_candidates_cache[type_name] = candidate + return constant + end end raise NameError.new("uninitialized constant #{candidates.first}", candidates.first) diff --git a/activerecord/lib/active_record/internal_metadata.rb b/activerecord/lib/active_record/internal_metadata.rb index 20d61dba67..25ee9d6bfe 100644 --- a/activerecord/lib/active_record/internal_metadata.rb +++ b/activerecord/lib/active_record/internal_metadata.rb @@ -23,7 +23,7 @@ module ActiveRecord end def table_exists? - ActiveSupport::Deprecation.silence { connection.table_exists?(table_name) } + connection.table_exists?(table_name) end # Creates an internal metadata table with columns +key+ and +value+ diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb index 82882469e3..2659c60f1f 100644 --- a/activerecord/lib/active_record/locking/optimistic.rb +++ b/activerecord/lib/active_record/locking/optimistic.rb @@ -69,7 +69,7 @@ module ActiveRecord send(lock_col + "=", previous_lock_value + 1) end - def _create_record(attribute_names = self.attribute_names, *) # :nodoc: + def _create_record(attribute_names = self.attribute_names, *) if locking_enabled? # We always want to persist the locking version, even if we don't detect # a change from the default, since the database might have no default @@ -78,7 +78,7 @@ module ActiveRecord super end - def _update_record(attribute_names = self.attribute_names) #:nodoc: + def _update_record(attribute_names = self.attribute_names) return super unless locking_enabled? lock_col = self.class.locking_column diff --git a/activerecord/lib/active_record/log_subscriber.rb b/activerecord/lib/active_record/log_subscriber.rb index 4b8d8d9105..ea101946f4 100644 --- a/activerecord/lib/active_record/log_subscriber.rb +++ b/activerecord/lib/active_record/log_subscriber.rb @@ -81,7 +81,7 @@ module ActiveRecord RED when /transaction\s*\Z/i CYAN - else + else MAGENTA end end diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index cc6bc17b9d..6e5f5fa2a7 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -522,7 +522,10 @@ module ActiveRecord def self.inherited(subclass) # :nodoc: super if subclass.superclass == Migration - subclass.include Compatibility::Legacy + raise StandardError, "Directly inheriting from ActiveRecord::Migration is not supported. " \ + "Please specify the Rails release the migration was written for:\n" \ + "\n" \ + " class #{self.class.name} < ActiveRecord::Migration[4.2]" end end @@ -1026,12 +1029,10 @@ module ActiveRecord end def get_all_versions(connection = Base.connection) - ActiveSupport::Deprecation.silence do - if connection.table_exists?(schema_migrations_table_name) - SchemaMigration.all.map { |x| x.version.to_i }.sort - else - [] - end + if connection.table_exists?(schema_migrations_table_name) + SchemaMigration.all.map { |x| x.version.to_i }.sort + else + [] end end @@ -1169,9 +1170,10 @@ module ActiveRecord def run_without_lock migration = migrations.detect { |m| m.version == @target_version } raise UnknownMigrationVersionError.new(@target_version) if migration.nil? - execute_migration_in_transaction(migration, @direction) + result = execute_migration_in_transaction(migration, @direction) record_environment + result end # Used for running multiple migrations up to or down to a certain value. @@ -1180,11 +1182,12 @@ module ActiveRecord raise UnknownMigrationVersionError.new(@target_version) end - runnable.each do |migration| + result = runnable.each do |migration| execute_migration_in_transaction(migration, @direction) end record_environment + result end # Stores the current environment in the database. diff --git a/activerecord/lib/active_record/migration/compatibility.rb b/activerecord/lib/active_record/migration/compatibility.rb index 9c357e1604..2904634eb7 100644 --- a/activerecord/lib/active_record/migration/compatibility.rb +++ b/activerecord/lib/active_record/migration/compatibility.rb @@ -13,7 +13,27 @@ module ActiveRecord V5_1 = Current - module FourTwoShared + class V5_0 < V5_1 + def create_table(table_name, options = {}) + if adapter_name == "PostgreSQL" + if options[:id] == :uuid && !options[:default] + options[:default] = "uuid_generate_v4()" + end + end + + # Since 5.1 Postgres adapter uses bigserial type for primary + # keys by default and MySQL uses bigint. This compat layer makes old migrations utilize + # serial/int type instead -- the way it used to work before 5.1. + if options[:id].blank? + options[:id] = :integer + options[:auto_increment] = true + end + + super + end + end + + class V4_2 < V5_0 module TableDefinition def references(*, **options) options[:index] ||= false @@ -101,46 +121,6 @@ module ActiveRecord index_name end end - - class V5_0 < V5_1 - def create_table(table_name, options = {}) - if adapter_name == "PostgreSQL" - if options[:id] == :uuid && !options[:default] - options[:default] = "uuid_generate_v4()" - end - end - - # Since 5.1 Postgres adapter uses bigserial type for primary - # keys by default and MySQL uses bigint. This compat layer makes old migrations utilize - # serial/int type instead -- the way it used to work before 5.1. - if options[:id].blank? - options[:id] = :integer - options[:auto_increment] = true - end - - super - end - end - - class V4_2 < V5_0 - # 4.2 is defined as a module because it needs to be shared with - # Legacy. When the time comes, V5_0 should be defined straight - # in its class. - include FourTwoShared - end - - module Legacy - include FourTwoShared - - def migrate(*) - ActiveSupport::Deprecation.warn \ - "Directly inheriting from ActiveRecord::Migration is deprecated. " \ - "Please specify the Rails release the migration was written for:\n" \ - "\n" \ - " class #{self.class.name} < ActiveRecord::Migration[4.2]" - super - end - end end end end diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index e983026961..01ecd79b8f 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -393,7 +393,7 @@ module ActiveRecord # update_only is true, and a <tt>:_destroy</tt> key set to a truthy value, # then the existing record will be marked for destruction. def assign_nested_attributes_for_one_to_one_association(association_name, attributes) - options = self.nested_attributes_options[association_name] + options = nested_attributes_options[association_name] if attributes.respond_to?(:permitted?) attributes = attributes.to_h end @@ -452,7 +452,7 @@ module ActiveRecord # { id: '2', _destroy: true } # ]) def assign_nested_attributes_for_collection_association(association_name, attributes_collection) - options = self.nested_attributes_options[association_name] + options = nested_attributes_options[association_name] if attributes_collection.respond_to?(:permitted?) attributes_collection = attributes_collection.to_h end @@ -562,7 +562,7 @@ module ActiveRecord def call_reject_if(association_name, attributes) return false if will_be_destroyed?(association_name, attributes) - case callback = self.nested_attributes_options[association_name][:reject_if] + case callback = nested_attributes_options[association_name][:reject_if] when Symbol method(callback).arity == 0 ? send(callback) : send(callback, attributes) when Proc diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 60d8e95b21..19fe9632ca 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -338,10 +338,10 @@ module ActiveRecord self end - # Wrapper around #increment that saves the record. This method differs from - # its non-bang version in that it passes through the attribute setter. - # Saving is not subjected to validation checks. Returns +true+ if the - # record could be saved. + # Wrapper around #increment that writes the update to the database. + # Only +attribute+ is updated; the record itself is not saved. + # This means that any other modified attributes will still be dirty. + # Validations and callbacks are skipped. Returns +self+. def increment!(attribute, by = 1) increment(attribute, by) change = public_send(attribute) - (attribute_in_database(attribute.to_s) || 0) @@ -357,10 +357,10 @@ module ActiveRecord increment(attribute, -by) end - # Wrapper around #decrement that saves the record. This method differs from - # its non-bang version in the sense that it passes through the attribute setter. - # Saving is not subjected to validation checks. Returns +true+ if the - # record could be saved. + # Wrapper around #decrement that writes the update to the database. + # Only +attribute+ is updated; the record itself is not saved. + # This means that any other modified attributes will still be dirty. + # Validations and callbacks are skipped. Returns +self+. def decrement!(attribute, by = 1) increment!(attribute, -by) end @@ -392,8 +392,8 @@ module ActiveRecord # Reloads the record from the database. # - # This method finds record by its primary key (which could be assigned manually) and - # modifies the receiver in-place: + # This method finds the record by its primary key (which could be assigned + # manually) and modifies the receiver in-place: # # account = Account.new # # => #<Account id: nil, email: nil> diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index 2701c5bca9..0276d41494 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -87,8 +87,8 @@ module ActiveRecord if File.file?(filename) cache = YAML.load(File.read(filename)) if cache.version == ActiveRecord::Migrator.current_version - self.connection.schema_cache = cache - self.connection_pool.schema_cache = cache.dup + connection.schema_cache = cache + connection_pool.schema_cache = cache.dup else warn "Ignoring db/schema_cache.yml because it has expired. The current schema version is #{ActiveRecord::Migrator.current_version}, but the one in the cache is #{cache.version}." end diff --git a/activerecord/lib/active_record/railties/controller_runtime.rb b/activerecord/lib/active_record/railties/controller_runtime.rb index adb3c6c4e6..8658188623 100644 --- a/activerecord/lib/active_record/railties/controller_runtime.rb +++ b/activerecord/lib/active_record/railties/controller_runtime.rb @@ -6,10 +6,14 @@ module ActiveRecord module ControllerRuntime #:nodoc: extend ActiveSupport::Concern + # TODO Change this to private once we've dropped Ruby 2.2 support. + # Workaround for Ruby 2.2 "private attribute?" warning. protected attr_internal :db_runtime + private + def process_action(action, *args) # We also need to reset the runtime before each action # because of queries in middleware or in cases we are streaming diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 25d79a6c7d..246d330b76 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -269,10 +269,7 @@ db_namespace = namespace :db do task dump: [:environment, :load_config] do conn = ActiveRecord::Base.connection filename = File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, "schema_cache.yml") - - conn.schema_cache.clear! - conn.data_sources.each { |table| conn.schema_cache.add(table) } - open(filename, "wb") { |f| f.write(YAML.dump(conn.schema_cache)) } + ActiveRecord::Tasks::DatabaseTasks.dump_schema_cache(conn, filename) end desc "Clears a db/schema_cache.yml file." @@ -312,14 +309,6 @@ db_namespace = namespace :db do end namespace :test do - - task :deprecated do - Rake.application.top_level_tasks.grep(/^db:test:/).each do |task| - $stderr.puts "WARNING: #{task} is deprecated. The Rails test helper now maintains " \ - "your test schema automatically, see the release notes for details." - end - end - # desc "Recreate the test database from the current schema" task load: %w(db:test:purge) do case ActiveRecord::Base.schema_format @@ -348,22 +337,6 @@ db_namespace = namespace :db do ActiveRecord::Tasks::DatabaseTasks.load_schema ActiveRecord::Base.configurations["test"], :sql, ENV["SCHEMA"] end - # desc "Recreate the test database from a fresh schema" - task clone: %w(db:test:deprecated environment) do - case ActiveRecord::Base.schema_format - when :ruby - db_namespace["test:clone_schema"].invoke - when :sql - db_namespace["test:clone_structure"].invoke - end - end - - # desc "Recreate the test database from a fresh schema.rb file" - task clone_schema: %w(db:test:deprecated db:schema:dump db:test:load_schema) - - # desc "Recreate the test database from a fresh structure.sql file" - task clone_structure: %w(db:test:deprecated db:structure:dump db:test:load_structure) - # desc "Empty the test database" task purge: %w(environment load_config check_protected_environments) do ActiveRecord::Tasks::DatabaseTasks.purge ActiveRecord::Base.configurations["test"] diff --git a/activerecord/lib/active_record/readonly_attributes.rb b/activerecord/lib/active_record/readonly_attributes.rb index 8ff265bdfa..6274996ab8 100644 --- a/activerecord/lib/active_record/readonly_attributes.rb +++ b/activerecord/lib/active_record/readonly_attributes.rb @@ -11,7 +11,7 @@ module ActiveRecord # Attributes listed as readonly will be used to create a new record but update operations will # ignore these fields. def attr_readonly(*attributes) - self._attr_readonly = Set.new(attributes.map(&:to_s)) + (self._attr_readonly || []) + self._attr_readonly = Set.new(attributes.map(&:to_s)) + (_attr_readonly || []) end # Returns an array of all the attributes that have been specified as readonly. diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 5bfc6727d8..2c8c4b6297 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -364,6 +364,17 @@ module ActiveRecord @constructable = calculate_constructable(macro, options) @association_scope_cache = {} @scope_lock = Mutex.new + + if options[:class_name] && options[:class_name].class == Class + ActiveSupport::Deprecation.warn(<<-MSG.squish) + Passing a class to the `class_name` is deprecated and will raise + an ArgumentError in Rails 5.2. It eagerloads more classes than + necessary and potentially creates circular dependencies. + + Please pass the class name as a string: + `#{macro} :#{name}, class_name: '#{options[:class_name]}'` + MSG + end end def association_scope_cache(conn, owner) @@ -878,15 +889,13 @@ module ActiveRecord } if names.length > 1 - example_options = options.dup - example_options[:source] = source_reflection_names.first - ActiveSupport::Deprecation.warn \ - "Ambiguous source reflection for through association. Please " \ - "specify a :source directive on your declaration like:\n" \ - "\n" \ - " class #{active_record.name} < ActiveRecord::Base\n" \ - " #{macro} :#{name}, #{example_options}\n" \ - " end" + raise AmbiguousSourceReflectionForThroughAssociation.new( + active_record.name, + macro, + name, + options, + source_reflection_names + ) end @source_reflection_name = names.first @@ -933,6 +942,14 @@ module ActiveRecord raise HasOneThroughCantAssociateThroughCollection.new(active_record.name, self, through_reflection) end + if parent_reflection.nil? + reflections = active_record.reflections.keys.map(&:to_sym) + + if reflections.index(through_reflection.name) > reflections.index(name) + raise HasManyThroughOrderError.new(active_record.name, self, through_reflection) + end + end + check_validity_of_inverse! end @@ -963,8 +980,7 @@ module ActiveRecord end end - protected - + private def actual_source_reflection # FIXME: this is a horrible name source_reflection.send(:actual_source_reflection) end @@ -975,7 +991,6 @@ module ActiveRecord def inverse_name; delegate_reflection.send(:inverse_name); end - private def derive_class_name # get the class_name of the belongs_to association of the through reflection options[:source_type] || source_reflection.class_name diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 4e941cf2df..61ee09bcc8 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -418,8 +418,7 @@ module ActiveRecord records.each { |record| record.update(attributes) } else if ActiveRecord::Base === id - id = id.id - ActiveSupport::Deprecation.warn(<<-MSG.squish) + raise ArgumentError, <<-MSG.squish You are passing an instance of ActiveRecord::Base to `update`. Please pass the id of the object by calling `.id`. MSG @@ -446,16 +445,8 @@ module ActiveRecord # ==== Examples # # Person.where(age: 0..18).destroy_all - def destroy_all(conditions = nil) - if conditions - ActiveSupport::Deprecation.warn(<<-MESSAGE.squish) - Passing conditions to destroy_all is deprecated and will be removed in Rails 5.1. - To achieve the same use where(conditions).destroy_all. - MESSAGE - where(conditions).destroy_all - else - records.each(&:destroy).tap { reset } - end + def destroy_all + records.each(&:destroy).tap { reset } end # Destroy an object (or multiple objects) that has the given id. The object is instantiated first, @@ -503,7 +494,7 @@ module ActiveRecord # # Post.limit(100).delete_all # # => ActiveRecord::ActiveRecordError: delete_all doesn't support limit - def delete_all(conditions = nil) + def delete_all invalid_methods = INVALID_METHODS_FOR_DELETE_ALL.select do |method| value = get_value(method) SINGLE_VALUE_METHODS.include?(method) ? value : value.any? @@ -512,27 +503,19 @@ module ActiveRecord raise ActiveRecordError.new("delete_all doesn't support #{invalid_methods.join(', ')}") end - if conditions - ActiveSupport::Deprecation.warn(<<-MESSAGE.squish) - Passing conditions to delete_all is deprecated and will be removed in Rails 5.1. - To achieve the same use where(conditions).delete_all. - MESSAGE - where(conditions).delete_all - else - stmt = Arel::DeleteManager.new - stmt.from(table) + stmt = Arel::DeleteManager.new + stmt.from(table) - if has_join_values? - @klass.connection.join_to_delete(stmt, arel, arel_attribute(primary_key)) - else - stmt.wheres = arel.constraints - end + if has_join_values? + @klass.connection.join_to_delete(stmt, arel, arel_attribute(primary_key)) + else + stmt.wheres = arel.constraints + end - affected = @klass.connection.delete(stmt, "SQL", bound_attributes) + affected = @klass.connection.delete(stmt, "SQL", bound_attributes) - reset - affected - end + reset + affected end # Deletes the row with a primary key matching the +id+ argument, using a @@ -630,15 +613,6 @@ module ActiveRecord includes_values & joins_values end - # {#uniq}[rdoc-ref:QueryMethods#uniq] and - # {#uniq!}[rdoc-ref:QueryMethods#uniq!] are silently deprecated. - # #uniq_value delegates to #distinct_value to maintain backwards compatibility. - # Use #distinct_value instead. - def uniq_value - distinct_value - end - deprecate uniq_value: :distinct_value - # Compares two relations for equality. def ==(other) case other diff --git a/activerecord/lib/active_record/relation/batches.rb b/activerecord/lib/active_record/relation/batches.rb index 4b2987ac6d..76031515fd 100644 --- a/activerecord/lib/active_record/relation/batches.rb +++ b/activerecord/lib/active_record/relation/batches.rb @@ -260,7 +260,7 @@ module ActiveRecord end def act_on_ignored_order(error_on_ignore) - raise_error = (error_on_ignore.nil? ? self.klass.error_on_ignored_order : error_on_ignore) + raise_error = (error_on_ignore.nil? ? klass.error_on_ignored_order : error_on_ignore) if raise_error raise ArgumentError.new(ORDER_IGNORE_MESSAGE) diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index 827688a663..35c670f1a1 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -193,7 +193,7 @@ module ActiveRecord # If #count is used with #distinct (i.e. `relation.distinct.count`) it is # considered distinct. - distinct = self.distinct_value + distinct = distinct_value if operation == "count" column_name ||= select_for_count diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb index 4b9310b225..f965818ed2 100644 --- a/activerecord/lib/active_record/relation/delegation.rb +++ b/activerecord/lib/active_record/relation/delegation.rb @@ -15,7 +15,10 @@ module ActiveRecord delegate = Class.new(klass) { include ClassSpecificRelation } - const_set klass.name.gsub("::".freeze, "_".freeze), delegate + mangled_name = klass.name.gsub("::".freeze, "_".freeze) + const_set mangled_name, delegate + private_constant mangled_name + cache[klass] = delegate end end @@ -78,7 +81,7 @@ module ActiveRecord end end - protected + private def method_missing(method, *args, &block) if @klass.respond_to?(method) @@ -110,7 +113,7 @@ module ActiveRecord arel.respond_to?(method, include_private) end - protected + private def method_missing(method, *args, &block) if @klass.respond_to?(method) diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 5e456452e9..6663bdb244 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -152,14 +152,6 @@ module ActiveRecord result = result.reverse_order! limit ? result.reverse : result.first - rescue ActiveRecord::IrreversibleOrderError - ActiveSupport::Deprecation.warn(<<-WARNING.squish) - Finding a last element by loading the relation when SQL ORDER - can not be reversed is deprecated. - Rails 5.1 will raise ActiveRecord::IrreversibleOrderError in this case. - Please call `to_a.last` if you still want to load the relation. - WARNING - find_last(limit) end # Same as #last but raises ActiveRecord::RecordNotFound if no record @@ -309,8 +301,7 @@ module ActiveRecord # Person.exists? def exists?(conditions = :none) if Base === conditions - conditions = conditions.id - ActiveSupport::Deprecation.warn(<<-MSG.squish) + raise ArgumentError, <<-MSG.squish You are passing an instance of ActiveRecord::Base to `exists?`. Please pass the id of the object by calling `.id`. MSG @@ -439,143 +430,140 @@ module ActiveRecord reflections.none?(&:collection?) end - protected + private - def find_with_ids(*ids) - raise UnknownPrimaryKey.new(@klass) if primary_key.nil? + def find_with_ids(*ids) + raise UnknownPrimaryKey.new(@klass) if primary_key.nil? - expects_array = ids.first.kind_of?(Array) - return ids.first if expects_array && ids.first.empty? + expects_array = ids.first.kind_of?(Array) + return ids.first if expects_array && ids.first.empty? - ids = ids.flatten.compact.uniq + ids = ids.flatten.compact.uniq - case ids.size - when 0 - raise RecordNotFound, "Couldn't find #{@klass.name} without an ID" - when 1 - result = find_one(ids.first) - expects_array ? [ result ] : result - else - find_some(ids) + case ids.size + when 0 + raise RecordNotFound, "Couldn't find #{@klass.name} without an ID" + when 1 + result = find_one(ids.first) + expects_array ? [ result ] : result + else + find_some(ids) + end + rescue ::RangeError + raise RecordNotFound, "Couldn't find #{@klass.name} with an out of range ID" end - rescue ::RangeError - raise RecordNotFound, "Couldn't find #{@klass.name} with an out of range ID" - end - def find_one(id) - if ActiveRecord::Base === id - id = id.id - ActiveSupport::Deprecation.warn(<<-MSG.squish) - You are passing an instance of ActiveRecord::Base to `find`. - Please pass the id of the object by calling `.id`. - MSG - end + def find_one(id) + if ActiveRecord::Base === id + raise ArgumentError, <<-MSG.squish + You are passing an instance of ActiveRecord::Base to `find`. + Please pass the id of the object by calling `.id`. + MSG + end - relation = where(primary_key => id) - record = relation.take + relation = where(primary_key => id) + record = relation.take - raise_record_not_found_exception!(id, 0, 1) unless record + raise_record_not_found_exception!(id, 0, 1) unless record - record - end + record + end - def find_some(ids) - return find_some_ordered(ids) unless order_values.present? + def find_some(ids) + return find_some_ordered(ids) unless order_values.present? - result = where(primary_key => ids).to_a + result = where(primary_key => ids).to_a - expected_size = - if limit_value && ids.size > limit_value - limit_value - else - ids.size - end + expected_size = + if limit_value && ids.size > limit_value + limit_value + else + ids.size + end - # 11 ids with limit 3, offset 9 should give 2 results. - if offset_value && (ids.size - offset_value < expected_size) - expected_size = ids.size - offset_value - end + # 11 ids with limit 3, offset 9 should give 2 results. + if offset_value && (ids.size - offset_value < expected_size) + expected_size = ids.size - offset_value + end - if result.size == expected_size - result - else - raise_record_not_found_exception!(ids, result.size, expected_size) + if result.size == expected_size + result + else + raise_record_not_found_exception!(ids, result.size, expected_size) + end end - end - def find_some_ordered(ids) - ids = ids.slice(offset_value || 0, limit_value || ids.size) || [] + def find_some_ordered(ids) + ids = ids.slice(offset_value || 0, limit_value || ids.size) || [] - result = except(:limit, :offset).where(primary_key => ids).records + result = except(:limit, :offset).where(primary_key => ids).records - if result.size == ids.size - pk_type = @klass.type_for_attribute(primary_key) + if result.size == ids.size + pk_type = @klass.type_for_attribute(primary_key) - records_by_id = result.index_by(&:id) - ids.map { |id| records_by_id.fetch(pk_type.cast(id)) } - else - raise_record_not_found_exception!(ids, result.size, ids.size) + records_by_id = result.index_by(&:id) + ids.map { |id| records_by_id.fetch(pk_type.cast(id)) } + else + raise_record_not_found_exception!(ids, result.size, ids.size) + end end - end - def find_take - if loaded? - records.first - else - @take ||= limit(1).records.first + def find_take + if loaded? + records.first + else + @take ||= limit(1).records.first + end end - end - def find_take_with_limit(limit) - if loaded? - records.take(limit) - else - limit(limit).to_a + def find_take_with_limit(limit) + if loaded? + records.take(limit) + else + limit(limit).to_a + end end - end - def find_nth(index) - @offsets[offset_index + index] ||= find_nth_with_limit(index, 1).first - end + def find_nth(index) + @offsets[offset_index + index] ||= find_nth_with_limit(index, 1).first + end - def find_nth_with_limit(index, limit) - if loaded? - records[index, limit] || [] - else - relation = if order_values.empty? && primary_key - order(arel_attribute(primary_key).asc) + def find_nth_with_limit(index, limit) + if loaded? + records[index, limit] || [] else - self + relation = if order_values.empty? && primary_key + order(arel_attribute(primary_key).asc) + else + self + end + + relation = relation.offset(offset_index + index) unless index.zero? + relation.limit(limit).to_a end - - relation = relation.offset(offset_index + index) unless index.zero? - relation.limit(limit).to_a end - end - def find_nth_from_last(index) - if loaded? - records[-index] - else - relation = if order_values.empty? && primary_key - order(arel_attribute(primary_key).asc) + def find_nth_from_last(index) + if loaded? + records[-index] else - self + relation = if order_values.empty? && primary_key + order(arel_attribute(primary_key).asc) + else + self + end + + relation.to_a[-index] + # TODO: can be made more performant on large result sets by + # for instance, last(index)[-index] (which would require + # refactoring the last(n) finder method to make test suite pass), + # or by using a combination of reverse_order, limit, and offset, + # e.g., reverse_order.offset(index-1).first end - - relation.to_a[-index] - # TODO: can be made more performant on large result sets by - # for instance, last(index)[-index] (which would require - # refactoring the last(n) finder method to make test suite pass), - # or by using a combination of reverse_order, limit, and offset, - # e.g., reverse_order.offset(index-1).first end - end - - private - def find_last(limit) - limit ? records.last(limit) : records.last - end + def find_last(limit) + limit ? records.last(limit) : records.last + end end end diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb index 780a1ee422..18ae10a652 100644 --- a/activerecord/lib/active_record/relation/predicate_builder.rb +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -4,7 +4,6 @@ module ActiveRecord require "active_record/relation/predicate_builder/association_query_handler" require "active_record/relation/predicate_builder/base_handler" require "active_record/relation/predicate_builder/basic_object_handler" - require "active_record/relation/predicate_builder/class_handler" require "active_record/relation/predicate_builder/polymorphic_array_handler" require "active_record/relation/predicate_builder/range_handler" require "active_record/relation/predicate_builder/relation_handler" @@ -16,7 +15,6 @@ module ActiveRecord @handlers = [] register_handler(BasicObject, BasicObjectHandler.new) - register_handler(Class, ClassHandler.new(self)) register_handler(Base, BaseHandler.new(self)) register_handler(Range, RangeHandler.new) register_handler(RangeHandler::RangeWithBinds, RangeHandler.new) @@ -66,6 +64,8 @@ module ActiveRecord handler_for(value).call(attribute, value) end + # TODO Change this to private once we've dropped Ruby 2.2 support. + # Workaround for Ruby 2.2 "private attribute?" warning. protected attr_reader :table diff --git a/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb index 6400caba06..88b6c37d43 100644 --- a/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb +++ b/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb @@ -29,6 +29,8 @@ module ActiveRecord array_predicates.inject { |composite, predicate| composite.or(predicate) } end + # TODO Change this to private once we've dropped Ruby 2.2 support. + # Workaround for Ruby 2.2 "private attribute?" warning. protected attr_reader :predicate_builder diff --git a/activerecord/lib/active_record/relation/predicate_builder/association_query_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/association_query_handler.rb index 7e20cb2c63..29860ec677 100644 --- a/activerecord/lib/active_record/relation/predicate_builder/association_query_handler.rb +++ b/activerecord/lib/active_record/relation/predicate_builder/association_query_handler.rb @@ -28,6 +28,8 @@ module ActiveRecord predicate_builder.build_from_hash(queries) end + # TODO Change this to private once we've dropped Ruby 2.2 support. + # Workaround for Ruby 2.2 "private attribute?" warning. protected attr_reader :predicate_builder @@ -68,9 +70,6 @@ module ActiveRecord case value when Relation value.klass.base_class - when Array - val = value.compact.first - val.class.base_class if val.is_a?(Base) when Base value.class.base_class end diff --git a/activerecord/lib/active_record/relation/predicate_builder/base_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/base_handler.rb index 65c5159704..3bb1037885 100644 --- a/activerecord/lib/active_record/relation/predicate_builder/base_handler.rb +++ b/activerecord/lib/active_record/relation/predicate_builder/base_handler.rb @@ -9,6 +9,8 @@ module ActiveRecord predicate_builder.build(attribute, value.id) end + # TODO Change this to private once we've dropped Ruby 2.2 support. + # Workaround for Ruby 2.2 "private attribute?" warning. protected attr_reader :predicate_builder diff --git a/activerecord/lib/active_record/relation/predicate_builder/class_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/class_handler.rb deleted file mode 100644 index 0a6574fcf1..0000000000 --- a/activerecord/lib/active_record/relation/predicate_builder/class_handler.rb +++ /dev/null @@ -1,27 +0,0 @@ -module ActiveRecord - class PredicateBuilder - class ClassHandler # :nodoc: - def initialize(predicate_builder) - @predicate_builder = predicate_builder - end - - def call(attribute, value) - print_deprecation_warning - predicate_builder.build(attribute, value.name) - end - - protected - - attr_reader :predicate_builder - - private - - def print_deprecation_warning - ActiveSupport::Deprecation.warn(<<-MSG.squish) - Passing a class as a value in an Active Record query is deprecated and - will be removed. Pass a string instead. - MSG - end - end - end -end diff --git a/activerecord/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb index 0c7f92b3d0..335124c952 100644 --- a/activerecord/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +++ b/activerecord/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb @@ -21,6 +21,8 @@ module ActiveRecord end end + # TODO Change this to private once we've dropped Ruby 2.2 support. + # Workaround for Ruby 2.2 "private attribute?" warning. protected attr_reader :predicate_builder diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 5f5d8ceea3..4ee413c805 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -3,7 +3,6 @@ require "active_record/relation/query_attribute" require "active_record/relation/where_clause" require "active_record/relation/where_clause_factory" require "active_model/forbidden_attributes_protection" -require "active_support/core_ext/string/filters" module ActiveRecord module QueryMethods @@ -76,7 +75,7 @@ module ActiveRecord end def bound_attributes - if limit_value && !string_containing_comma?(limit_value) + if limit_value limit_bind = Attribute.with_cast_value( "LIMIT".freeze, connection.sanitize_limit(limit_value), @@ -243,9 +242,7 @@ module ActiveRecord def select(*fields) if block_given? if fields.any? - ActiveSupport::Deprecation.warn(<<-WARNING.squish) - When select is called with a block, it ignores other arguments. This behavior is now deprecated and will result in an ArgumentError in Rails 5.1. You can safely remove the arguments to resolve the deprecation warning because they do not have any effect on the output of the call to the select method with a block. - WARNING + raise ArgumentError, "`select' with block doesn't take arguments." end return super() @@ -659,7 +656,7 @@ module ActiveRecord end self.where_clause = self.where_clause.or(other.where_clause) - self.having_clause = self.having_clause.or(other.having_clause) + self.having_clause = having_clause.or(other.having_clause) self end @@ -690,13 +687,6 @@ module ActiveRecord end def limit!(value) # :nodoc: - if string_containing_comma?(value) - # Remove `string_containing_comma?` when removing this deprecation - ActiveSupport::Deprecation.warn(<<-WARNING.squish) - Passing a string to limit in the form "1,2" is deprecated and will be - removed in Rails 5.1. Please call `offset` explicitly instead. - WARNING - end self.limit_value = value self end @@ -848,16 +838,12 @@ module ActiveRecord def distinct(value = true) spawn.distinct!(value) end - alias uniq distinct - deprecate uniq: :distinct # Like #distinct, but modifies relation in place. def distinct!(value = true) # :nodoc: self.distinct_value = value self end - alias uniq! distinct! - deprecate uniq!: :distinct! # Used to extend a scope with additional methods, either through # a module or through a block provided. @@ -958,13 +944,7 @@ module ActiveRecord arel.where(where_clause.ast) unless where_clause.empty? arel.having(having_clause.ast) unless having_clause.empty? - if limit_value - if string_containing_comma?(limit_value) - arel.take(connection.sanitize_limit(limit_value)) - else - arel.take(Arel::Nodes::BindParam.new) - end - end + arel.take(Arel::Nodes::BindParam.new) if limit_value arel.skip(Arel::Nodes::BindParam.new) if offset_value arel.group(*arel_columns(group_values.uniq.reject(&:blank?))) unless group_values.empty? @@ -1192,10 +1172,6 @@ module ActiveRecord end alias having_clause_factory where_clause_factory - def string_containing_comma?(value) - ::String === value && value.include?(",") - end - def default_value_for(name) case name when :create_with diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb index 190e339ea8..ada89b5ec3 100644 --- a/activerecord/lib/active_record/relation/spawn_methods.rb +++ b/activerecord/lib/active_record/relation/spawn_methods.rb @@ -66,7 +66,7 @@ module ActiveRecord private - def relation_with(values) # :nodoc: + def relation_with(values) result = Relation.create(klass, table, predicate_builder, values) result.extend(*extending_values) if extending_values.any? result diff --git a/activerecord/lib/active_record/relation/where_clause.rb b/activerecord/lib/active_record/relation/where_clause.rb index 402f8acfd1..ef0d059d1c 100644 --- a/activerecord/lib/active_record/relation/where_clause.rb +++ b/activerecord/lib/active_record/relation/where_clause.rb @@ -84,6 +84,8 @@ module ActiveRecord @empty ||= new([], []) end + # TODO Change this to private once we've dropped Ruby 2.2 support. + # Workaround for Ruby 2.2 "private attribute?" warning. protected attr_reader :predicates diff --git a/activerecord/lib/active_record/relation/where_clause_factory.rb b/activerecord/lib/active_record/relation/where_clause_factory.rb index 1e7deeffad..737bc278bd 100644 --- a/activerecord/lib/active_record/relation/where_clause_factory.rb +++ b/activerecord/lib/active_record/relation/where_clause_factory.rb @@ -27,6 +27,8 @@ module ActiveRecord WhereClause.new(parts, binds || []) end + # TODO Change this to private once we've dropped Ruby 2.2 support. + # Workaround for Ruby 2.2 "private attribute?" warning. protected attr_reader :klass, :predicate_builder diff --git a/activerecord/lib/active_record/sanitization.rb b/activerecord/lib/active_record/sanitization.rb index 3d52dc44cf..427c0019c6 100644 --- a/activerecord/lib/active_record/sanitization.rb +++ b/activerecord/lib/active_record/sanitization.rb @@ -4,7 +4,7 @@ module ActiveRecord extend ActiveSupport::Concern module ClassMethods - protected + private # Accepts an array or string of SQL conditions and sanitizes # them into a valid SQL fragment for a WHERE clause. @@ -20,7 +20,7 @@ module ActiveRecord # # sanitize_sql_for_conditions("name='foo''bar' and group_id='4'") # # => "name='foo''bar' and group_id='4'" - def sanitize_sql_for_conditions(condition) + def sanitize_sql_for_conditions(condition) # :doc: return nil if condition.blank? case condition @@ -46,7 +46,7 @@ module ActiveRecord # # sanitize_sql_for_assignment("name=NULL and group_id='4'") # # => "name=NULL and group_id='4'" - def sanitize_sql_for_assignment(assignments, default_table_name = self.table_name) + def sanitize_sql_for_assignment(assignments, default_table_name = table_name) # :doc: case assignments when Array; sanitize_sql_array(assignments) when Hash; sanitize_sql_hash_for_assignment(assignments, default_table_name) @@ -62,7 +62,7 @@ module ActiveRecord # # sanitize_sql_for_order("id ASC") # # => "id ASC" - def sanitize_sql_for_order(condition) + def sanitize_sql_for_order(condition) # :doc: if condition.is_a?(Array) && condition.first.to_s.include?("?") sanitize_sql_array(condition) else @@ -85,7 +85,7 @@ module ActiveRecord # # { address: Address.new("813 abc st.", "chicago") } # # => { address_street: "813 abc st.", address_city: "chicago" } - def expand_hash_conditions_for_aggregates(attrs) + def expand_hash_conditions_for_aggregates(attrs) # :doc: expanded_attrs = {} attrs.each do |attr, value| if aggregation = reflect_on_aggregation(attr.to_sym) @@ -108,7 +108,7 @@ module ActiveRecord # # sanitize_sql_hash_for_assignment({ status: nil, group_id: 1 }, "posts") # # => "`posts`.`status` = NULL, `posts`.`group_id` = 1" - def sanitize_sql_hash_for_assignment(attrs, table) + def sanitize_sql_hash_for_assignment(attrs, table) # :doc: c = connection attrs.map do |attr, value| value = type_for_attribute(attr.to_s).serialize(value) @@ -130,7 +130,7 @@ module ActiveRecord # # sanitize_sql_like("snake_cased_string", "!") # # => "snake!_cased!_string" - def sanitize_sql_like(string, escape_character = "\\") + def sanitize_sql_like(string, escape_character = "\\") # :doc: pattern = Regexp.union(escape_character, "%", "_") string.gsub(pattern) { |x| [escape_character, x].join } end @@ -146,7 +146,7 @@ module ActiveRecord # # sanitize_sql_array(["name='%s' and group_id='%s'", "foo'bar", 4]) # # => "name='foo''bar' and group_id='4'" - def sanitize_sql_array(ary) + def sanitize_sql_array(ary) # :doc: statement, *values = ary if values.first.is_a?(Hash) && /:\w+/.match?(statement) replace_named_bind_variables(statement, values.first) @@ -159,7 +159,7 @@ module ActiveRecord end end - def replace_bind_variables(statement, values) # :nodoc: + def replace_bind_variables(statement, values) raise_if_bind_arity_mismatch(statement, statement.count("?"), values.size) bound = values.dup c = connection @@ -168,7 +168,7 @@ module ActiveRecord end end - def replace_bind_variable(value, c = connection) # :nodoc: + def replace_bind_variable(value, c = connection) if ActiveRecord::Relation === value value.to_sql else @@ -176,7 +176,7 @@ module ActiveRecord end end - def replace_named_bind_variables(statement, bind_vars) # :nodoc: + def replace_named_bind_variables(statement, bind_vars) statement.gsub(/(:?):([a-zA-Z]\w*)/) do |match| if $1 == ":" # skip postgresql casts match # return the whole match @@ -188,7 +188,7 @@ module ActiveRecord end end - def quote_bound_value(value, c = connection) # :nodoc: + def quote_bound_value(value, c = connection) if value.respond_to?(:map) && !value.acts_like?(:string) if value.respond_to?(:empty?) && value.empty? c.quote(nil) @@ -200,7 +200,7 @@ module ActiveRecord end end - def raise_if_bind_arity_mismatch(statement, expected, provided) # :nodoc: + def raise_if_bind_arity_mismatch(statement, expected, provided) unless expected == provided raise PreparedStatementInvalid, "wrong number of bind variables (#{provided} for #{expected}) in: #{statement}" end diff --git a/activerecord/lib/active_record/schema.rb b/activerecord/lib/active_record/schema.rb index 99e54a8b24..7a2bc9c8af 100644 --- a/activerecord/lib/active_record/schema.rb +++ b/activerecord/lib/active_record/schema.rb @@ -61,7 +61,7 @@ module ActiveRecord # # ActiveRecord::Schema.new.migrations_paths # # => ["db/migrate"] # Rails migration path by default. - def migrations_paths # :nodoc: + def migrations_paths ActiveRecord::Migrator.migrations_paths end end diff --git a/activerecord/lib/active_record/schema_migration.rb b/activerecord/lib/active_record/schema_migration.rb index 99b23e5593..5efbcff96a 100644 --- a/activerecord/lib/active_record/schema_migration.rb +++ b/activerecord/lib/active_record/schema_migration.rb @@ -17,7 +17,7 @@ module ActiveRecord end def table_exists? - ActiveSupport::Deprecation.silence { connection.table_exists?(table_name) } + connection.table_exists?(table_name) end def create_table diff --git a/activerecord/lib/active_record/scoping/default.rb b/activerecord/lib/active_record/scoping/default.rb index 9d8253faa3..2daa48859a 100644 --- a/activerecord/lib/active_record/scoping/default.rb +++ b/activerecord/lib/active_record/scoping/default.rb @@ -44,7 +44,7 @@ module ActiveRecord self.current_scope = nil end - protected + private # Use this macro in your model to set a default scope for all operations on # the model. @@ -87,7 +87,7 @@ module ActiveRecord # # Should return a scope, you can call 'super' here etc. # end # end - def default_scope(scope = nil) + def default_scope(scope = nil) # :doc: scope = Proc.new if block_given? if scope.is_a?(Relation) || !scope.respond_to?(:call) @@ -101,7 +101,7 @@ module ActiveRecord self.default_scopes += [scope] end - def build_default_scope(base_rel = nil) # :nodoc: + def build_default_scope(base_rel = nil) return if abstract_class? if default_scope_override.nil? @@ -122,18 +122,18 @@ module ActiveRecord end end - def ignore_default_scope? # :nodoc: + def ignore_default_scope? ScopeRegistry.value_for(:ignore_default_scope, base_class) end - def ignore_default_scope=(ignore) # :nodoc: + def ignore_default_scope=(ignore) ScopeRegistry.set_value_for(:ignore_default_scope, base_class, ignore) end # The ignore_default_scope flag is used to prevent an infinite recursion # situation where a default scope references a scope which has a default # scope which references a scope... - def evaluate_default_scope # :nodoc: + def evaluate_default_scope return if ignore_default_scope? begin diff --git a/activerecord/lib/active_record/scoping/named.rb b/activerecord/lib/active_record/scoping/named.rb index 6af84c1266..27cdf8cb7e 100644 --- a/activerecord/lib/active_record/scoping/named.rb +++ b/activerecord/lib/active_record/scoping/named.rb @@ -171,14 +171,14 @@ module ActiveRecord end end - protected + private - def valid_scope_name?(name) - if respond_to?(name, true) && logger - logger.warn "Creating scope :#{name}. " \ - "Overwriting existing method #{self.name}.#{name}." + def valid_scope_name?(name) + if respond_to?(name, true) && logger + logger.warn "Creating scope :#{name}. " \ + "Overwriting existing method #{self.name}.#{name}." + end end - end end end end diff --git a/activerecord/lib/active_record/secure_token.rb b/activerecord/lib/active_record/secure_token.rb index 7606961e2e..115799cc20 100644 --- a/activerecord/lib/active_record/secure_token.rb +++ b/activerecord/lib/active_record/secure_token.rb @@ -27,7 +27,7 @@ module ActiveRecord # Load securerandom only when has_secure_token is used. require "active_support/core_ext/securerandom" define_method("regenerate_#{attribute}") { update! attribute => self.class.generate_unique_secure_token } - before_create { self.send("#{attribute}=", self.class.generate_unique_secure_token) unless self.send("#{attribute}?") } + before_create { send("#{attribute}=", self.class.generate_unique_secure_token) unless send("#{attribute}?") } end def generate_unique_secure_token diff --git a/activerecord/lib/active_record/store.rb b/activerecord/lib/active_record/store.rb index 066573192e..d4be20d999 100644 --- a/activerecord/lib/active_record/store.rb +++ b/activerecord/lib/active_record/store.rb @@ -121,18 +121,17 @@ module ActiveRecord end end - protected - def read_store_attribute(store_attribute, key) + private + def read_store_attribute(store_attribute, key) # :doc: accessor = store_accessor_for(store_attribute) accessor.read(self, store_attribute, key) end - def write_store_attribute(store_attribute, key, value) + def write_store_attribute(store_attribute, key, value) # :doc: accessor = store_accessor_for(store_attribute) accessor.write(self, store_attribute, key, value) end - private def store_accessor_for(store_attribute) type_for_attribute(store_attribute.to_s).accessor end diff --git a/activerecord/lib/active_record/table_metadata.rb b/activerecord/lib/active_record/table_metadata.rb index 58184f3872..71efc1829a 100644 --- a/activerecord/lib/active_record/table_metadata.rb +++ b/activerecord/lib/active_record/table_metadata.rb @@ -44,7 +44,7 @@ module ActiveRecord end def associated_table(table_name) - association = klass._reflect_on_association(table_name) || klass._reflect_on_association(table_name.singularize) + association = klass._reflect_on_association(table_name) || klass._reflect_on_association(table_name.to_s.singularize) if !association && table_name == arel_table.name return self @@ -64,6 +64,8 @@ module ActiveRecord association && association.polymorphic? end + # TODO Change this to private once we've dropped Ruby 2.2 support. + # Workaround for Ruby 2.2 "private attribute?" warning. protected attr_reader :klass, :arel_table, :association diff --git a/activerecord/lib/active_record/tasks/database_tasks.rb b/activerecord/lib/active_record/tasks/database_tasks.rb index c6204ac36f..bdb5184599 100644 --- a/activerecord/lib/active_record/tasks/database_tasks.rb +++ b/activerecord/lib/active_record/tasks/database_tasks.rb @@ -1,5 +1,3 @@ -require "active_support/core_ext/string/filters" - module ActiveRecord module Tasks # :nodoc: class DatabaseAlreadyExists < StandardError; end # :nodoc: @@ -35,6 +33,16 @@ module ActiveRecord # # DatabaseTasks.create_current('production') module DatabaseTasks + ## + # :singleton-method: + # Extra flags passed to database CLI tool (mysqldump/pg_dump) when calling db:structure:dump + mattr_accessor :structure_dump_flags, instance_accessor: false + + ## + # :singleton-method: + # Extra flags passed to database CLI tool when calling db:structure:load + mattr_accessor :structure_load_flags, instance_accessor: false + extend self attr_writer :current_config, :db_dir, :migrations_paths, :fixtures_path, :root, :env, :seed_loader @@ -204,13 +212,13 @@ module ActiveRecord def structure_dump(*arguments) configuration = arguments.first filename = arguments.delete_at 1 - class_for_adapter(configuration["adapter"]).new(*arguments).structure_dump(filename) + class_for_adapter(configuration["adapter"]).new(*arguments).structure_dump(filename, structure_dump_flags) end def structure_load(*arguments) configuration = arguments.first filename = arguments.delete_at 1 - class_for_adapter(configuration["adapter"]).new(*arguments).structure_load(filename) + class_for_adapter(configuration["adapter"]).new(*arguments).structure_load(filename, structure_load_flags) end def load_schema(configuration, format = ActiveRecord::Base.schema_format, file = nil) # :nodoc: @@ -231,14 +239,6 @@ module ActiveRecord ActiveRecord::InternalMetadata[:environment] = ActiveRecord::Migrator.current_environment end - def load_schema_for(*args) - ActiveSupport::Deprecation.warn(<<-MSG.squish) - This method was renamed to `#load_schema` and will be removed in the future. - Use `#load_schema` instead. - MSG - load_schema(*args) - end - def schema_file(format = ActiveRecord::Base.schema_format) case format when :ruby @@ -273,6 +273,16 @@ module ActiveRecord end end + # Dumps the schema cache in YAML format for the connection into the file + # + # ==== Examples: + # ActiveRecord::Tasks::DatabaseTasks.dump_schema_cache(ActiveRecord::Base.connection, "tmp/schema_dump.yaml") + def dump_schema_cache(conn, filename) + conn.schema_cache.clear! + conn.data_sources.each { |table| conn.schema_cache.add(table) } + open(filename, "wb") { |f| f.write(YAML.dump(conn.schema_cache)) } + end + private def class_for_adapter(adapter) diff --git a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb index 5cdb3d53f6..920830b9cf 100644 --- a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb +++ b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb @@ -53,21 +53,23 @@ module ActiveRecord connection.collation end - def structure_dump(filename) + def structure_dump(filename, extra_flags) args = prepare_command_options args.concat(["--result-file", "#{filename}"]) args.concat(["--no-data"]) args.concat(["--routines"]) args.concat(["--skip-comments"]) + args.concat(Array(extra_flags)) if extra_flags args.concat(["#{configuration['database']}"]) run_cmd("mysqldump", args, "dumping") end - def structure_load(filename) + def structure_load(filename, extra_flags) args = prepare_command_options args.concat(["--execute", %{SET FOREIGN_KEY_CHECKS = 0; SOURCE #{filename}; SET FOREIGN_KEY_CHECKS = 1}]) args.concat(["--database", "#{configuration['database']}"]) + args.concat(Array(extra_flags)) if extra_flags run_cmd("mysql", args, "loading") end diff --git a/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb b/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb index 4e9897f7b0..5155ced0e2 100644 --- a/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb +++ b/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb @@ -43,7 +43,7 @@ module ActiveRecord create true end - def structure_dump(filename) + def structure_dump(filename, extra_flags) set_psql_env search_path = \ @@ -57,6 +57,7 @@ module ActiveRecord end args = ["-s", "-x", "-O", "-f", filename] + args.concat(Array(extra_flags)) if extra_flags unless search_path.blank? args += search_path.split(",").map do |part| "--schema=#{part.strip}" @@ -67,9 +68,11 @@ module ActiveRecord File.open(filename, "a") { |f| f << "SET search_path TO #{connection.schema_search_path};\n\n" } end - def structure_load(filename) + def structure_load(filename, extra_flags) set_psql_env - args = [ "-v", ON_ERROR_STOP_1, "-q", "-f", filename, configuration["database"] ] + args = ["-v", ON_ERROR_STOP_1, "-q", "-f", filename] + args.concat(Array(extra_flags)) if extra_flags + args << configuration["database"] run_cmd("psql", args, "loading") end diff --git a/activerecord/lib/active_record/tasks/sqlite_database_tasks.rb b/activerecord/lib/active_record/tasks/sqlite_database_tasks.rb index 31f1b7efd4..1f756c2979 100644 --- a/activerecord/lib/active_record/tasks/sqlite_database_tasks.rb +++ b/activerecord/lib/active_record/tasks/sqlite_database_tasks.rb @@ -21,7 +21,7 @@ module ActiveRecord FileUtils.rm(file) rescue Errno::ENOENT => error - raise NoDatabaseError.new(error.message, error) + raise NoDatabaseError.new(error.message) end def purge @@ -35,14 +35,16 @@ module ActiveRecord connection.encoding end - def structure_dump(filename) + def structure_dump(filename, extra_flags) dbfile = configuration["database"] - `sqlite3 #{dbfile} .schema > #{filename}` + flags = extra_flags.join(" ") if extra_flags + `sqlite3 #{flags} #{dbfile} .schema > #{filename}` end - def structure_load(filename) + def structure_load(filename, extra_flags) dbfile = configuration["database"] - `sqlite3 #{dbfile} < "#{filename}"` + flags = extra_flags.join(" ") if extra_flags + `sqlite3 #{flags} #{dbfile} < "#{filename}"` end private diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb index 63100e38a1..09d8d1cdd4 100644 --- a/activerecord/lib/active_record/timestamp.rb +++ b/activerecord/lib/active_record/timestamp.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: true module ActiveRecord # = Active Record \Timestamp # @@ -51,15 +52,41 @@ module ActiveRecord clear_timestamp_attributes end + class_methods do + private + def timestamp_attributes_for_create_in_model + timestamp_attributes_for_create.select { |c| column_names.include?(c) } + end + + def timestamp_attributes_for_update_in_model + timestamp_attributes_for_update.select { |c| column_names.include?(c) } + end + + def all_timestamp_attributes_in_model + timestamp_attributes_for_create_in_model + timestamp_attributes_for_update_in_model + end + + def timestamp_attributes_for_create + ["created_at", "created_on"] + end + + def timestamp_attributes_for_update + ["updated_at", "updated_on"] + end + + def current_time_from_proper_timezone + default_timezone == :utc ? Time.now.utc : Time.now + end + end + private def _create_record if record_timestamps current_time = current_time_from_proper_timezone - all_timestamp_attributes.each do |column| - column = column.to_s - if has_attribute?(column) && !attribute_present?(column) + all_timestamp_attributes_in_model.each do |column| + if !attribute_present?(column) write_attribute(column, current_time) end end @@ -73,7 +100,6 @@ module ActiveRecord current_time = current_time_from_proper_timezone timestamp_attributes_for_update_in_model.each do |column| - column = column.to_s next if will_save_change_to_attribute?(column) write_attribute(column, current_time) end @@ -86,30 +112,22 @@ module ActiveRecord end def timestamp_attributes_for_create_in_model - timestamp_attributes_for_create.select { |c| self.class.column_names.include?(c.to_s) } + self.class.send(:timestamp_attributes_for_create_in_model) end def timestamp_attributes_for_update_in_model - timestamp_attributes_for_update.select { |c| self.class.column_names.include?(c.to_s) } + self.class.send(:timestamp_attributes_for_update_in_model) end def all_timestamp_attributes_in_model - timestamp_attributes_for_create_in_model + timestamp_attributes_for_update_in_model - end - - def timestamp_attributes_for_update - [:updated_at, :updated_on] - end - - def timestamp_attributes_for_create - [:created_at, :created_on] + self.class.send(:all_timestamp_attributes_in_model) end - def all_timestamp_attributes - timestamp_attributes_for_create + timestamp_attributes_for_update + def current_time_from_proper_timezone + self.class.send(:current_time_from_proper_timezone) end - def max_updated_column_timestamp(timestamp_names = timestamp_attributes_for_update) + def max_updated_column_timestamp(timestamp_names = self.class.send(:timestamp_attributes_for_update)) timestamp_names .map { |attr| self[attr] } .compact @@ -117,10 +135,6 @@ module ActiveRecord .max end - def current_time_from_proper_timezone - self.class.default_timezone == :utc ? Time.now.utc : Time.now - end - # Clear attributes and changed_attributes def clear_timestamp_attributes all_timestamp_attributes_in_model.each do |attribute_name| diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb index ce939c8b97..56b75540e3 100644 --- a/activerecord/lib/active_record/transactions.rb +++ b/activerecord/lib/active_record/transactions.rb @@ -274,16 +274,6 @@ module ActiveRecord set_callback(:rollback_without_transaction_enrollment, :after, *args, &block) end - def raise_in_transactional_callbacks - ActiveSupport::Deprecation.warn("ActiveRecord::Base.raise_in_transactional_callbacks is deprecated and will be removed without replacement.") - true - end - - def raise_in_transactional_callbacks=(value) - ActiveSupport::Deprecation.warn("ActiveRecord::Base.raise_in_transactional_callbacks= is deprecated, has no effect and will be removed without replacement.") - value - end - private def set_options_for_callbacks!(args, enforced_options = {}) @@ -407,10 +397,10 @@ module ActiveRecord end end - protected + private # Save the new record state and id of a record so it can be restored later if a transaction fails. - def remember_transaction_record_state #:nodoc: + def remember_transaction_record_state @_start_transaction_state[:id] = id @_start_transaction_state.reverse_merge!( new_record: @new_record, @@ -421,18 +411,18 @@ module ActiveRecord end # Clear the new record state and id of a record. - def clear_transaction_record_state #:nodoc: + def clear_transaction_record_state @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1 force_clear_transaction_record_state if @_start_transaction_state[:level] < 1 end # Force to clear the transaction record state. - def force_clear_transaction_record_state #:nodoc: + def force_clear_transaction_record_state @_start_transaction_state.clear end # Restore the new record state and id of a record that was previously saved by a call to save_record_state. - def restore_transaction_record_state(force = false) #:nodoc: + def restore_transaction_record_state(force = false) unless @_start_transaction_state.empty? transaction_level = (@_start_transaction_state[:level] || 0) - 1 if transaction_level < 1 || force @@ -450,12 +440,12 @@ module ActiveRecord end # Determine if a record was created or destroyed in a transaction. State should be one of :new_record or :destroyed. - def transaction_record_state(state) #:nodoc: + def transaction_record_state(state) @_start_transaction_state[state] end # Determine if a transaction included an action for :create, :update, or :destroy. Used in filtering callbacks. - def transaction_include_any_action?(actions) #:nodoc: + def transaction_include_any_action?(actions) actions.any? do |action| case action when :create @@ -469,13 +459,11 @@ module ActiveRecord end end - private - - def set_transaction_state(state) # :nodoc: + def set_transaction_state(state) @transaction_state = state end - def has_transactional_callbacks? # :nodoc: + def has_transactional_callbacks? !_rollback_callbacks.empty? || !_commit_callbacks.empty? || !_before_commit_callbacks.empty? end diff --git a/activerecord/lib/active_record/type/adapter_specific_registry.rb b/activerecord/lib/active_record/type/adapter_specific_registry.rb index d0f9581576..7cc866f7a7 100644 --- a/activerecord/lib/active_record/type/adapter_specific_registry.rb +++ b/activerecord/lib/active_record/type/adapter_specific_registry.rb @@ -50,6 +50,8 @@ module ActiveRecord priority <=> other.priority end + # TODO Change this to private once we've dropped Ruby 2.2 support. + # Workaround for Ruby 2.2 "private attribute?" warning. protected attr_reader :name, :block, :adapter, :override @@ -110,6 +112,8 @@ module ActiveRecord super | 4 end + # TODO Change this to private once we've dropped Ruby 2.2 support. + # Workaround for Ruby 2.2 "private attribute?" warning. protected attr_reader :options, :klass diff --git a/activerecord/lib/active_record/type_caster/connection.rb b/activerecord/lib/active_record/type_caster/connection.rb index 6c54792e26..9f7bbe8843 100644 --- a/activerecord/lib/active_record/type_caster/connection.rb +++ b/activerecord/lib/active_record/type_caster/connection.rb @@ -12,6 +12,8 @@ module ActiveRecord connection.type_cast_from_column(column, value) end + # TODO Change this to private once we've dropped Ruby 2.2 support. + # Workaround for Ruby 2.2 "private attribute?" warning. protected attr_reader :table_name diff --git a/activerecord/lib/active_record/type_caster/map.rb b/activerecord/lib/active_record/type_caster/map.rb index 52529a6b42..9f79723125 100644 --- a/activerecord/lib/active_record/type_caster/map.rb +++ b/activerecord/lib/active_record/type_caster/map.rb @@ -11,6 +11,8 @@ module ActiveRecord type.serialize(value) end + # TODO Change this to private once we've dropped Ruby 2.2 support. + # Workaround for Ruby 2.2 "private attribute?" warning. protected attr_reader :types diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index c013a4518f..9633f226f0 100644 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -68,7 +68,7 @@ module ActiveRecord alias_method :validate, :valid? - protected + private def default_validation_context new_record? ? :create : :update @@ -78,7 +78,7 @@ module ActiveRecord raise(RecordInvalid.new(self)) end - def perform_validations(options = {}) # :nodoc: + def perform_validations(options = {}) options[:validate] == false || valid?(options[:context]) end end diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb index 512fdadacc..9e8edfbfaf 100644 --- a/activerecord/lib/active_record/validations/uniqueness.rb +++ b/activerecord/lib/active_record/validations/uniqueness.rb @@ -33,13 +33,13 @@ module ActiveRecord end end - protected + private # The check for an existing value should be run from a class that # isn't abstract. This means working down from the current class # (self), to the first non-abstract class. Since classes don't know # their subclasses, we have to build the hierarchy between self and # the record's class. - def find_finder_class_for(record) #:nodoc: + def find_finder_class_for(record) class_hierarchy = [record.class] while class_hierarchy.first != @klass @@ -49,7 +49,7 @@ module ActiveRecord class_hierarchy.detect { |klass| !klass.abstract_class? } end - def build_relation(klass, attribute, value) # :nodoc: + def build_relation(klass, attribute, value) if reflection = klass._reflect_on_association(attribute) attribute = reflection.foreign_key value = value.attributes[reflection.klass.primary_key] unless value.nil? diff --git a/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb b/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb index 12d1f58f67..8511531af7 100644 --- a/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb +++ b/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb @@ -13,9 +13,13 @@ module ActiveRecord migration_template @migration_template, "db/migrate/#{file_name}.rb" end + # TODO Change this to private once we've dropped Ruby 2.2 support. + # Workaround for Ruby 2.2 "private attribute?" warning. protected attr_reader :migration_action, :join_tables + private + # Sets the default migration template that is being used for the generation of the migration. # Depending on command line arguments, the migration template and the table name instance # variables are set up. @@ -52,7 +56,6 @@ module ActiveRecord end.to_sym end - private def attributes_with_index attributes.select { |a| !a.reference? && a.has_index? } end diff --git a/activerecord/lib/rails/generators/active_record/model/model_generator.rb b/activerecord/lib/rails/generators/active_record/model/model_generator.rb index f1ddc61688..a9df5212e3 100644 --- a/activerecord/lib/rails/generators/active_record/model/model_generator.rb +++ b/activerecord/lib/rails/generators/active_record/model/model_generator.rb @@ -33,7 +33,7 @@ module ActiveRecord hook_for :test_framework - protected + private def attributes_with_index attributes.select { |a| !a.reference? && a.has_index? } @@ -41,7 +41,7 @@ module ActiveRecord # FIXME: Change this file to a symlink once RubyGems 2.5.0 is required. def generate_application_record - if self.behavior == :invoke && !application_record_exist? + if behavior == :invoke && !application_record_exist? template "application_record.rb", application_record_file_name end end diff --git a/activerecord/test/cases/adapter_test.rb b/activerecord/test/cases/adapter_test.rb index 3fce0a1df1..9828e682ef 100644 --- a/activerecord/test/cases/adapter_test.rb +++ b/activerecord/test/cases/adapter_test.rb @@ -32,7 +32,7 @@ module ActiveRecord def test_tables tables = nil - ActiveSupport::Deprecation.silence { tables = @connection.tables } + tables = @connection.tables assert_includes tables, "accounts" assert_includes tables, "authors" assert_includes tables, "tasks" @@ -40,17 +40,11 @@ module ActiveRecord end def test_table_exists? - ActiveSupport::Deprecation.silence do - assert @connection.table_exists?("accounts") - assert @connection.table_exists?(:accounts) - assert_not @connection.table_exists?("nonexistingtable") - assert_not @connection.table_exists?("'") - assert_not @connection.table_exists?(nil) - end - end - - def test_table_exists_checking_both_tables_and_views_is_deprecated - assert_deprecated { @connection.table_exists?("accounts") } + assert @connection.table_exists?("accounts") + assert @connection.table_exists?(:accounts) + assert_not @connection.table_exists?("nonexistingtable") + assert_not @connection.table_exists?("'") + assert_not @connection.table_exists?(nil) end def test_data_sources @@ -294,16 +288,6 @@ module ActiveRecord assert_not_nil error.message end end - - if current_adapter?(:Mysql2Adapter, :SQLite3Adapter) - def test_tables_returning_both_tables_and_views_is_deprecated - assert_deprecated { @connection.tables } - end - end - - def test_passing_arguments_to_tables_is_deprecated - assert_deprecated { @connection.tables(:books) } - end end class AdapterTestWithoutTransaction < ActiveRecord::TestCase diff --git a/activerecord/test/cases/adapters/mysql2/connection_test.rb b/activerecord/test/cases/adapters/mysql2/connection_test.rb index 9c109d8a24..bae283a048 100644 --- a/activerecord/test/cases/adapters/mysql2/connection_test.rb +++ b/activerecord/test/cases/adapters/mysql2/connection_test.rb @@ -66,9 +66,10 @@ class Mysql2ConnectionTest < ActiveRecord::Mysql2TestCase def test_execute_after_disconnect @connection.disconnect! - assert_raise(ActiveRecord::StatementInvalid) do + error = assert_raise(ActiveRecord::StatementInvalid) do @connection.execute("SELECT 1") end + assert_kind_of Mysql2::Error, error.cause end def test_quote_after_disconnect @@ -186,7 +187,7 @@ class Mysql2ConnectionTest < ActiveRecord::Mysql2TestCase "expected release_advisory_lock to return false when there was no lock to release" end - protected + private def test_lock_free(lock_name) @connection.select_value("SELECT IS_FREE_LOCK(#{@connection.quote(lock_name)})") == 1 diff --git a/activerecord/test/cases/adapters/mysql2/json_test.rb b/activerecord/test/cases/adapters/mysql2/json_test.rb index 5d4db1be91..6954006003 100644 --- a/activerecord/test/cases/adapters/mysql2/json_test.rb +++ b/activerecord/test/cases/adapters/mysql2/json_test.rb @@ -93,7 +93,7 @@ if ActiveRecord::Base.connection.supports_json? def test_null_json @connection.execute "insert into json_data_type (payload) VALUES(null)" x = JsonDataType.first - assert_equal(nil, x.payload) + assert_nil(x.payload) end def test_select_array_json_value @@ -111,7 +111,7 @@ if ActiveRecord::Base.connection.supports_json? def test_select_nil_json_after_update json = JsonDataType.create(payload: "foo") x = JsonDataType.where(payload: nil).first - assert_equal(nil, x) + assert_nil(x) json.update_attributes payload: nil x = JsonDataType.where(payload: nil).first diff --git a/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb b/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb index 268800d538..a0823be143 100644 --- a/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb +++ b/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb @@ -15,6 +15,7 @@ class Mysql2UnsignedTypeTest < ActiveRecord::Mysql2TestCase t.bigint :unsigned_bigint, unsigned: true t.float :unsigned_float, unsigned: true t.decimal :unsigned_decimal, unsigned: true, precision: 10, scale: 2 + t.column :unsigned_zerofill, "int unsigned zerofill" end end @@ -48,7 +49,6 @@ class Mysql2UnsignedTypeTest < ActiveRecord::Mysql2TestCase t.unsigned_bigint :unsigned_bigint_t t.unsigned_float :unsigned_float_t t.unsigned_decimal :unsigned_decimal_t, precision: 10, scale: 2 - t.column :unsigned_zerofill, "int unsigned zerofill" end @connection.columns("unsigned_types").select { |c| /^unsigned_/.match?(c.name) }.each do |column| diff --git a/activerecord/test/cases/adapters/postgresql/array_test.rb b/activerecord/test/cases/adapters/postgresql/array_test.rb index 680dad9706..c78c6178ff 100644 --- a/activerecord/test/cases/adapters/postgresql/array_test.rb +++ b/activerecord/test/cases/adapters/postgresql/array_test.rb @@ -16,10 +16,12 @@ class PostgresqlArrayTest < ActiveRecord::PostgreSQLTestCase @connection.transaction do @connection.create_table("pg_arrays") do |t| - t.string "tags", array: true + t.string "tags", array: true, limit: 255 t.integer "ratings", array: true t.datetime :datetimes, array: true t.hstore :hstores, array: true + t.decimal :decimals, array: true, default: [], precision: 10, scale: 2 + t.timestamp :timestamps, array: true, default: [], precision: 6 end end PgArray.reset_column_information @@ -34,7 +36,7 @@ class PostgresqlArrayTest < ActiveRecord::PostgreSQLTestCase def test_column assert_equal :string, @column.type - assert_equal "character varying", @column.sql_type + assert_equal "character varying(255)", @column.sql_type assert @column.array? assert_not @type.binary? @@ -110,8 +112,9 @@ class PostgresqlArrayTest < ActiveRecord::PostgreSQLTestCase def test_schema_dump_with_shorthand output = dump_table_schema "pg_arrays" - assert_match %r[t\.string\s+"tags",\s+array: true], output + assert_match %r[t\.string\s+"tags",\s+limit: 255,\s+array: true], output assert_match %r[t\.integer\s+"ratings",\s+array: true], output + assert_match %r[t\.decimal\s+"decimals",\s+precision: 10,\s+scale: 2,\s+default: \[\],\s+array: true], output end def test_select_with_strings @@ -211,7 +214,7 @@ class PostgresqlArrayTest < ActiveRecord::PostgreSQLTestCase x = PgArray.create!(tags: tags) x.reload - assert_equal x.tags_before_type_cast, PgArray.type_for_attribute("tags").serialize(tags) + refute x.changed? end def test_quoting_non_standard_delimiters @@ -219,9 +222,10 @@ class PostgresqlArrayTest < ActiveRecord::PostgreSQLTestCase oid = ActiveRecord::ConnectionAdapters::PostgreSQL::OID comma_delim = oid::Array.new(ActiveRecord::Type::String.new, ",") semicolon_delim = oid::Array.new(ActiveRecord::Type::String.new, ";") + conn = PgArray.connection - assert_equal %({"hello,",world;}), comma_delim.serialize(strings) - assert_equal %({hello,;"world;"}), semicolon_delim.serialize(strings) + assert_equal %({"hello,",world;}), conn.type_cast(comma_delim.serialize(strings)) + assert_equal %({hello,;"world;"}), conn.type_cast(semicolon_delim.serialize(strings)) end def test_mutate_array @@ -317,6 +321,15 @@ class PostgresqlArrayTest < ActiveRecord::PostgreSQLTestCase assert_equal [arrays_of_utf8_strings], @type.deserialize(@type.serialize([arrays_of_utf8_strings])) end + def test_precision_is_respected_on_timestamp_columns + time = Time.now.change(usec: 123) + record = PgArray.create!(timestamps: [time]) + + assert_equal 123, record.timestamps.first.usec + record.reload + assert_equal 123, record.timestamps.first.usec + end + private def assert_cycle(field, array) # test creation diff --git a/activerecord/test/cases/adapters/postgresql/bytea_test.rb b/activerecord/test/cases/adapters/postgresql/bytea_test.rb index dc0df8715a..5c207116c4 100644 --- a/activerecord/test/cases/adapters/postgresql/bytea_test.rb +++ b/activerecord/test/cases/adapters/postgresql/bytea_test.rb @@ -52,7 +52,7 @@ class PostgresqlByteaTest < ActiveRecord::PostgreSQLTestCase end def test_type_case_nil - assert_equal(nil, @type.deserialize(nil)) + assert_nil(@type.deserialize(nil)) end def test_read_value @@ -66,7 +66,7 @@ class PostgresqlByteaTest < ActiveRecord::PostgreSQLTestCase def test_read_nil_value @connection.execute "insert into bytea_data_type (payload) VALUES (null)" record = ByteaDataType.first - assert_equal(nil, record.payload) + assert_nil(record.payload) record.delete end @@ -106,8 +106,8 @@ class PostgresqlByteaTest < ActiveRecord::PostgreSQLTestCase def test_write_nil record = ByteaDataType.create(payload: nil) assert_not record.new_record? - assert_equal(nil, record.payload) - assert_equal(nil, ByteaDataType.where(id: record.id).first.payload) + assert_nil(record.payload) + assert_nil(ByteaDataType.where(id: record.id).first.payload) end class Serializer diff --git a/activerecord/test/cases/adapters/postgresql/connection_test.rb b/activerecord/test/cases/adapters/postgresql/connection_test.rb index 48c82cb7b9..075301d6d5 100644 --- a/activerecord/test/cases/adapters/postgresql/connection_test.rb +++ b/activerecord/test/cases/adapters/postgresql/connection_test.rb @@ -90,17 +90,17 @@ module ActiveRecord end def test_tables_logs_name - ActiveSupport::Deprecation.silence { @connection.tables("hello") } + @connection.tables assert_equal "SCHEMA", @subscriber.logged[0][1] end def test_indexes_logs_name - @connection.indexes("items", "hello") + assert_deprecated { @connection.indexes("items", "hello") } assert_equal "SCHEMA", @subscriber.logged[0][1] end def test_table_exists_logs_name - ActiveSupport::Deprecation.silence { @connection.table_exists?("items") } + @connection.table_exists?("items") assert_equal "SCHEMA", @subscriber.logged[0][1] end @@ -245,7 +245,7 @@ module ActiveRecord end end - protected + private def with_warning_suppression log_level = @connection.client_min_messages diff --git a/activerecord/test/cases/adapters/postgresql/geometric_test.rb b/activerecord/test/cases/adapters/postgresql/geometric_test.rb index a65d4d1ad9..c1f3a4ae2c 100644 --- a/activerecord/test/cases/adapters/postgresql/geometric_test.rb +++ b/activerecord/test/cases/adapters/postgresql/geometric_test.rb @@ -96,7 +96,7 @@ class PostgresqlPointTest < ActiveRecord::PostgreSQLTestCase assert_nothing_raised { PostgresqlPoint.new(x: "") } p = PostgresqlPoint.new(x: "") - assert_equal nil, p.x + assert_nil p.x end def test_array_of_points_round_trip diff --git a/activerecord/test/cases/adapters/postgresql/hstore_test.rb b/activerecord/test/cases/adapters/postgresql/hstore_test.rb index 1f35300739..f9cce10fb8 100644 --- a/activerecord/test/cases/adapters/postgresql/hstore_test.rb +++ b/activerecord/test/cases/adapters/postgresql/hstore_test.rb @@ -171,6 +171,25 @@ if ActiveRecord::Base.connection.supports_extensions? assert_not hstore.changed? end + def test_dirty_from_user_equal + settings = { "alongkey" => "anything", "key" => "value" } + hstore = Hstore.create!(settings: settings) + + hstore.settings = { "key" => "value", "alongkey" => "anything" } + assert_equal settings, hstore.settings + refute hstore.changed? + end + + def test_hstore_dirty_from_database_equal + settings = { "alongkey" => "anything", "key" => "value" } + hstore = Hstore.create!(settings: settings) + hstore.reload + + assert_equal settings, hstore.settings + hstore.settings = settings + refute hstore.changed? + end + def test_gen1 assert_equal('" "=>""', @type.serialize(" " => "")) end diff --git a/activerecord/test/cases/adapters/postgresql/json_test.rb b/activerecord/test/cases/adapters/postgresql/json_test.rb index 991dedfdf1..93558ac4d2 100644 --- a/activerecord/test/cases/adapters/postgresql/json_test.rb +++ b/activerecord/test/cases/adapters/postgresql/json_test.rb @@ -110,7 +110,7 @@ module PostgresqlJSONSharedTestCases def test_null_json @connection.execute "insert into json_data_type (payload) VALUES(null)" x = JsonDataType.first - assert_equal(nil, x.payload) + assert_nil(x.payload) end def test_select_nil_json_after_create @@ -122,7 +122,7 @@ module PostgresqlJSONSharedTestCases def test_select_nil_json_after_update json = JsonDataType.create(payload: "foo") x = JsonDataType.where(payload: nil).first - assert_equal(nil, x) + assert_nil(x) json.update_attributes payload: nil x = JsonDataType.where(payload: nil).first diff --git a/activerecord/test/cases/adapters/postgresql/quoting_test.rb b/activerecord/test/cases/adapters/postgresql/quoting_test.rb index a9e81ab3d8..141baffa5b 100644 --- a/activerecord/test/cases/adapters/postgresql/quoting_test.rb +++ b/activerecord/test/cases/adapters/postgresql/quoting_test.rb @@ -36,7 +36,7 @@ module ActiveRecord def test_quote_bit_string value = "'); SELECT * FROM users; /*\n01\n*/--" type = OID::Bit.new - assert_equal nil, @conn.quote(type.serialize(value)) + assert_nil @conn.quote(type.serialize(value)) end end end diff --git a/activerecord/test/cases/adapters/postgresql/transaction_test.rb b/activerecord/test/cases/adapters/postgresql/transaction_test.rb index f130e344c4..9b42d0383d 100644 --- a/activerecord/test/cases/adapters/postgresql/transaction_test.rb +++ b/activerecord/test/cases/adapters/postgresql/transaction_test.rb @@ -89,7 +89,7 @@ module ActiveRecord end end - protected + private def with_warning_suppression log_level = ActiveRecord::Base.connection.client_min_messages diff --git a/activerecord/test/cases/adapters/postgresql/type_lookup_test.rb b/activerecord/test/cases/adapters/postgresql/type_lookup_test.rb index bd45a9daa0..784d77a8d1 100644 --- a/activerecord/test/cases/adapters/postgresql/type_lookup_test.rb +++ b/activerecord/test/cases/adapters/postgresql/type_lookup_test.rb @@ -19,7 +19,7 @@ class PostgresqlTypeLookupTest < ActiveRecord::PostgreSQLTestCase big_array = [123456789123456789] assert_raises(ActiveModel::RangeError) { int_array.serialize(big_array) } - assert_equal "{123456789123456789}", bigint_array.serialize(big_array) + assert_equal "{123456789123456789}", @connection.type_cast(bigint_array.serialize(big_array)) end test "range types correctly respect registration of subtypes" do diff --git a/activerecord/test/cases/adapters/postgresql/utils_test.rb b/activerecord/test/cases/adapters/postgresql/utils_test.rb index 7f6ea3887d..9f9e3bda2f 100644 --- a/activerecord/test/cases/adapters/postgresql/utils_test.rb +++ b/activerecord/test/cases/adapters/postgresql/utils_test.rb @@ -56,7 +56,7 @@ class PostgreSQLNameTest < ActiveRecord::PostgreSQLTestCase test "can be used as hash key" do hash = { Name.new("schema", "article_seq") => "success" } assert_equal "success", hash[Name.new("schema", "article_seq")] - assert_equal nil, hash[Name.new("schema", "articles")] - assert_equal nil, hash[Name.new("public", "article_seq")] + assert_nil hash[Name.new("schema", "articles")] + assert_nil hash[Name.new("public", "article_seq")] end end diff --git a/activerecord/test/cases/adapters/postgresql/uuid_test.rb b/activerecord/test/cases/adapters/postgresql/uuid_test.rb index 4604c2eb3b..f34d50e25c 100644 --- a/activerecord/test/cases/adapters/postgresql/uuid_test.rb +++ b/activerecord/test/cases/adapters/postgresql/uuid_test.rb @@ -71,12 +71,12 @@ class PostgresqlUUIDTest < ActiveRecord::PostgreSQLTestCase def test_treat_blank_uuid_as_nil UUIDType.create! guid: "" - assert_equal(nil, UUIDType.last.guid) + assert_nil(UUIDType.last.guid) end def test_treat_invalid_uuid_as_nil uuid = UUIDType.create! guid: "foobar" - assert_equal(nil, uuid.guid) + assert_nil(uuid.guid) end def test_invalid_uuid_dont_modify_before_type_cast @@ -210,7 +210,7 @@ class PostgresqlUUIDGenerationTest < ActiveRecord::PostgreSQLTestCase def test_pk_and_sequence_for_uuid_primary_key pk, seq = connection.pk_and_sequence_for("pg_uuids") assert_equal "id", pk - assert_equal nil, seq + assert_nil seq end def test_schema_dumper_for_uuid_primary_key diff --git a/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb b/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb index 8342b05870..91967c1e33 100644 --- a/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb +++ b/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb @@ -75,7 +75,7 @@ class CopyTableTest < ActiveRecord::SQLite3TestCase test_copy_table "binaries", "binaries2" end -protected +private def copy_table(from, to, options = {}) @connection.copy_table(from, to, { temporary: true }.merge(options)) end diff --git a/activerecord/test/cases/adapters/sqlite3/quoting_test.rb b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb index 80a37e83ff..9750840051 100644 --- a/activerecord/test/cases/adapters/sqlite3/quoting_test.rb +++ b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb @@ -37,7 +37,7 @@ class SQLite3QuotingTest < ActiveRecord::SQLite3TestCase end def test_type_cast_nil - assert_equal nil, @conn.type_cast(nil) + assert_nil @conn.type_cast(nil) end def test_type_cast_true diff --git a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb index 0526191952..a6afb7816b 100644 --- a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb +++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb @@ -267,9 +267,9 @@ module ActiveRecord def test_tables with_example_table do - ActiveSupport::Deprecation.silence { assert_equal %w{ ex }, @conn.tables } + assert_equal %w{ ex }, @conn.tables with_example_table "id integer PRIMARY KEY AUTOINCREMENT, number integer", "people" do - ActiveSupport::Deprecation.silence { assert_equal %w{ ex people }.sort, @conn.tables.sort } + assert_equal %w{ ex people }.sort, @conn.tables.sort end end end @@ -277,19 +277,17 @@ module ActiveRecord def test_tables_logs_name sql = <<-SQL SELECT name FROM sqlite_master - WHERE type IN ('table','view') AND name <> 'sqlite_sequence' + WHERE type = 'table' AND name <> 'sqlite_sequence' SQL assert_logged [[sql.squish, "SCHEMA", []]] do - ActiveSupport::Deprecation.silence do - @conn.tables("hello") - end + @conn.tables end end def test_indexes_logs_name with_example_table do assert_logged [["PRAGMA index_list(\"ex\")", "SCHEMA", []]] do - @conn.indexes("ex", "hello") + assert_deprecated { @conn.indexes("ex", "hello") } end end end @@ -298,12 +296,10 @@ module ActiveRecord with_example_table do sql = <<-SQL SELECT name FROM sqlite_master - WHERE type IN ('table','view') AND name <> 'sqlite_sequence' AND name = 'ex' + WHERE type = 'table' AND name <> 'sqlite_sequence' AND name = 'ex' SQL assert_logged [[sql.squish, "SCHEMA", []]] do - ActiveSupport::Deprecation.silence do - assert @conn.table_exists?("ex") - end + assert @conn.table_exists?("ex") end end end diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb index 81a2a161f2..5875a1871f 100644 --- a/activerecord/test/cases/associations/belongs_to_associations_test.rb +++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb @@ -1057,7 +1057,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase comment.parent = nil comment.save! - assert_equal nil, comment.reload.parent + assert_nil comment.reload.parent assert_equal 0, comments(:greetings).reload.children_count end @@ -1131,12 +1131,6 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase Column.create! record: record assert_equal 1, Column.count end - - def test_association_force_reload_with_only_true_is_deprecated - client = Client.find(3) - - assert_deprecated { client.firm(true) } - end end class BelongsToWithForeignKeyTest < ActiveRecord::TestCase diff --git a/activerecord/test/cases/associations/eager_load_nested_include_test.rb b/activerecord/test/cases/associations/eager_load_nested_include_test.rb index ed1b0f5226..e9f551b6b2 100644 --- a/activerecord/test/cases/associations/eager_load_nested_include_test.rb +++ b/activerecord/test/cases/associations/eager_load_nested_include_test.rb @@ -12,7 +12,7 @@ module Remembered included do after_create :remember - protected + private def remember; self.class.remembered << self; end end diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb index dc04ccdccc..ff1bf8acd4 100644 --- a/activerecord/test/cases/associations/eager_test.rb +++ b/activerecord/test/cases/associations/eager_test.rb @@ -241,7 +241,7 @@ class EagerAssociationTest < ActiveRecord::TestCase post = assert_queries(1) { Post.all.merge!(includes: { author_with_address: :author_address }).find(post.id) } # find the post, then find the author which is null so no query for the author or address assert_no_queries do - assert_equal nil, post.author_with_address + assert_nil post.author_with_address end end @@ -250,7 +250,7 @@ class EagerAssociationTest < ActiveRecord::TestCase sponsor.update!(sponsorable: nil) sponsor = assert_queries(1) { Sponsor.all.merge!(includes: :sponsorable).find(sponsor.id) } assert_no_queries do - assert_equal nil, sponsor.sponsorable + assert_nil sponsor.sponsorable end end @@ -261,7 +261,7 @@ class EagerAssociationTest < ActiveRecord::TestCase assert_nothing_raised { Sponsor.all.merge!(includes: :sponsorable).find(sponsor.id) } end assert_no_queries do - assert_equal nil, sponsor.sponsorable + assert_nil sponsor.sponsorable end end diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb index 4b7ac594cf..efd2124679 100644 --- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb @@ -86,8 +86,10 @@ class DeveloperWithSymbolClassName < Developer has_and_belongs_to_many :projects, class_name: :ProjectWithSymbolsForKeys end -class DeveloperWithConstantClassName < Developer - has_and_belongs_to_many :projects, class_name: ProjectWithSymbolsForKeys +ActiveSupport::Deprecation.silence do + class DeveloperWithConstantClassName < Developer + has_and_belongs_to_many :projects, class_name: ProjectWithSymbolsForKeys + end end class DeveloperWithExtendOption < Developer @@ -955,12 +957,6 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase end end - def test_association_force_reload_with_only_true_is_deprecated - developer = Developer.find(1) - - assert_deprecated { developer.projects(true) } - end - def test_alternate_database professor = Professor.create(name: "Plum") course = Course.create(name: "Forensics") diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index d657be71cc..cbecfa84ff 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -187,7 +187,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase ship.parts.clear part.reload - assert_equal nil, part.ship + assert_nil part.ship assert !part.updated_at_changed? end @@ -788,13 +788,6 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert_equal [1], posts(:welcome).comments.select { |c| c.id == 1 }.map(&:id) end - def test_select_with_block_and_specific_attributes - assert_deprecated do - comments = posts(:welcome).comments.select(:id, :body) { |c| c.id == 1 } - assert_equal [1], comments.map(&:id) - end - end - def test_select_without_foreign_key assert_equal companies(:first_firm).accounts.first.credit_limit, companies(:first_firm).accounts.select(:credit_limit).first.credit_limit end @@ -1582,26 +1575,6 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert firm.companies.exists?(name: "child") end - def test_restrict_with_error_is_deprecated_using_key_many - I18n.backend = I18n::Backend::Simple.new - I18n.backend.store_translations :en, activerecord: { errors: { messages: { restrict_dependent_destroy: { many: "message for deprecated key" } } } } - - firm = RestrictedWithErrorFirm.create!(name: "restrict") - firm.companies.create(name: "child") - - assert !firm.companies.empty? - - assert_deprecated { firm.destroy } - - assert !firm.errors.empty? - - assert_equal "message for deprecated key", firm.errors[:base].first - assert RestrictedWithErrorFirm.exists?(name: "restrict") - assert firm.companies.exists?(name: "child") - ensure - I18n.backend.reload! - end - def test_restrict_with_error firm = RestrictedWithErrorFirm.create!(name: "restrict") firm.companies.create(name: "child") @@ -2475,19 +2448,13 @@ class HasManyAssociationsTest < ActiveRecord::TestCase test "double insertion of new object to association when same association used in the after create callback of a new object" do reset_callbacks(:save, Bulb) do - Bulb.after_save { |record| record.car.bulbs.to_a } + Bulb.after_save { |record| record.car.bulbs.load } car = Car.create! car.bulbs << Bulb.new assert_equal 1, car.bulbs.size end end - def test_association_force_reload_with_only_true_is_deprecated - company = Company.find(1) - - assert_deprecated { company.clients_of_firm(true) } - end - class AuthorWithErrorDestroyingAssociation < ActiveRecord::Base self.table_name = "authors" has_many :posts_with_error_destroying, diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index 83b1f8d4d6..ac005b7010 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -402,7 +402,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase end end - assert_equal nil, reference.reload.job_id + assert_nil reference.reload.job_id ensure Reference.make_comments = false end @@ -423,7 +423,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase end # Check that the destroy callback on Reference did not run - assert_equal nil, person.reload.comments + assert_nil person.reload.comments ensure Reference.make_comments = false end @@ -485,7 +485,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase end references.each do |reference| - assert_equal nil, reference.reload.job_id + assert_nil reference.reload.job_id end end @@ -1204,12 +1204,6 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase assert_nil Club.new.special_favourites.distinct_value end - def test_association_force_reload_with_only_true_is_deprecated - post = Post.find(1) - - assert_deprecated { post.people(true) } - end - def test_has_many_through_do_not_cache_association_reader_if_the_though_method_has_default_scopes member = Member.create! club = Club.create! @@ -1237,4 +1231,12 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase ensure TenantMembership.current_member = nil end + + def test_incorrectly_ordered_through_associations + assert_raises(ActiveRecord::HasManyThroughOrderError) do + DeveloperWithIncorrectlyOrderedHasManyThrough.create( + companies: [Company.create] + ) + end + end end diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb index 48fbc5990c..aa910ba409 100644 --- a/activerecord/test/cases/associations/has_one_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_associations_test.rb @@ -186,25 +186,6 @@ class HasOneAssociationsTest < ActiveRecord::TestCase assert firm.account.present? end - def test_restrict_with_error_is_deprecated_using_key_one - I18n.backend = I18n::Backend::Simple.new - I18n.backend.store_translations :en, activerecord: { errors: { messages: { restrict_dependent_destroy: { one: "message for deprecated key" } } } } - - firm = RestrictedWithErrorFirm.create!(name: "restrict") - firm.create_account(credit_limit: 10) - - assert_not_nil firm.account - - assert_deprecated { firm.destroy } - - assert !firm.errors.empty? - assert_equal "message for deprecated key", firm.errors[:base].first - assert RestrictedWithErrorFirm.exists?(name: "restrict") - assert firm.account.present? - ensure - I18n.backend.reload! - end - def test_restrict_with_error firm = RestrictedWithErrorFirm.create!(name: "restrict") firm.create_account(credit_limit: 10) @@ -664,15 +645,10 @@ class HasOneAssociationsTest < ActiveRecord::TestCase end end - def test_association_force_reload_with_only_true_is_deprecated - firm = Firm.find(1) - - assert_deprecated { firm.account(true) } - end - class SpecialBook < ActiveRecord::Base self.table_name = "books" belongs_to :author, class_name: "SpecialAuthor" + has_one :subscription, class_name: "SpecialSupscription", foreign_key: "subscriber_id" end class SpecialAuthor < ActiveRecord::Base @@ -680,6 +656,11 @@ class HasOneAssociationsTest < ActiveRecord::TestCase has_one :book, class_name: "SpecialBook", foreign_key: "author_id" end + class SpecialSupscription < ActiveRecord::Base + self.table_name = "subscriptions" + belongs_to :book, class_name: "SpecialBook" + end + def test_assocation_enum_works_properly author = SpecialAuthor.create!(name: "Test") book = SpecialBook.create!(status: "published") @@ -687,4 +668,15 @@ class HasOneAssociationsTest < ActiveRecord::TestCase refute_equal 0, SpecialAuthor.joins(:book).where(books: { status: "published" }).count end + + def test_assocation_enum_works_properly_with_nested_join + author = SpecialAuthor.create!(name: "Test") + book = SpecialBook.create!(status: "published") + author.book = book + + where_clause = { books: { subscriptions: { subscriber_id: nil } } } + assert_nothing_raised do + SpecialAuthor.joins(book: :subscription).where.not(where_clause) + end + end end diff --git a/activerecord/test/cases/associations/has_one_through_associations_test.rb b/activerecord/test/cases/associations/has_one_through_associations_test.rb index 6ba062a248..432c3526a5 100644 --- a/activerecord/test/cases/associations/has_one_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_through_associations_test.rb @@ -82,7 +82,7 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase def test_set_record_to_nil_should_delete_association @member.club = nil @member.reload - assert_equal nil, @member.current_membership + assert_nil @member.current_membership assert_nil @member.club end @@ -110,12 +110,12 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase # conditions on the through table assert_equal clubs(:moustache_club), Member.all.merge!(includes: :favourite_club).find(@member.id).favourite_club memberships(:membership_of_favourite_club).update_columns(favourite: false) - assert_equal nil, Member.all.merge!(includes: :favourite_club).find(@member.id).reload.favourite_club + assert_nil Member.all.merge!(includes: :favourite_club).find(@member.id).reload.favourite_club # conditions on the source table assert_equal clubs(:moustache_club), Member.all.merge!(includes: :hairy_club).find(@member.id).hairy_club clubs(:moustache_club).update_columns(name: "Association of Clean-Shaven Persons") - assert_equal nil, Member.all.merge!(includes: :hairy_club).find(@member.id).reload.hairy_club + assert_nil Member.all.merge!(includes: :hairy_club).find(@member.id).reload.hairy_club end def test_has_one_through_polymorphic_with_source_type diff --git a/activerecord/test/cases/associations_test.rb b/activerecord/test/cases/associations_test.rb index c095b3a91c..a223b4338f 100644 --- a/activerecord/test/cases/associations_test.rb +++ b/activerecord/test/cases/associations_test.rb @@ -88,10 +88,10 @@ class AssociationsTest < ActiveRecord::TestCase assert firm.clients.empty?, "New firm should have cached no client objects" assert_equal 0, firm.clients.size, "New firm should have cached 0 clients count" - ActiveSupport::Deprecation.silence do - assert !firm.clients(true).empty?, "New firm should have reloaded client objects" - assert_equal 1, firm.clients(true).size, "New firm should have reloaded clients count" - end + firm.clients.reload + + assert !firm.clients.empty?, "New firm should have reloaded client objects" + assert_equal 1, firm.clients.size, "New firm should have reloaded clients count" end def test_using_limitable_reflections_helper @@ -104,19 +104,6 @@ class AssociationsTest < ActiveRecord::TestCase assert !using_limitable_reflections.call(mixed_reflections), "No collection associations (has many style) should pass" end - def test_force_reload_is_uncached - firm = Firm.create!("name" => "A New Firm, Inc") - Client.create!("name" => "TheClient.com", :firm => firm) - - ActiveSupport::Deprecation.silence do - ActiveRecord::Base.cache do - firm.clients.each {} - assert_queries(0) { assert_not_nil firm.clients.each {} } - assert_queries(1) { assert_not_nil firm.clients(true).each {} } - end - end - end - def test_association_with_references firm = companies(:first_firm) assert_includes firm.association_with_references.references_values, "foo" diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index 4ac604a164..3dc0c0ce53 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -92,7 +92,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase test "attribute keys on a new instance" do t = Topic.new - assert_equal nil, t.title, "The topics table has a title column, so it should be nil" + assert_nil t.title, "The topics table has a title column, so it should be nil" assert_raise(NoMethodError) { t.title2 } end @@ -156,7 +156,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase keyboard = Keyboard.create keyboard.key_number = "10" assert_equal "10", keyboard.id_before_type_cast - assert_equal nil, keyboard.read_attribute_before_type_cast("id") + assert_nil keyboard.read_attribute_before_type_cast("id") assert_equal "10", keyboard.read_attribute_before_type_cast("key_number") assert_equal "10", keyboard.read_attribute_before_type_cast(:key_number) end @@ -213,7 +213,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase record.written_on = "345643456" assert_equal "345643456", record.written_on_before_type_cast - assert_equal nil, record.written_on + assert_nil record.written_on record.written_on = "2009-10-11 12:13:14" assert_equal "2009-10-11 12:13:14", record.written_on_before_type_cast @@ -754,7 +754,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase test "time zone-aware attributes do not recurse infinitely on invalid values" do in_time_zone "Pacific Time (US & Canada)" do record = @target.new(bonus_time: []) - assert_equal nil, record.bonus_time + assert_nil record.bonus_time end end diff --git a/activerecord/test/cases/attribute_set_test.rb b/activerecord/test/cases/attribute_set_test.rb index 059b5b2401..bd4b200735 100644 --- a/activerecord/test/cases/attribute_set_test.rb +++ b/activerecord/test/cases/attribute_set_test.rb @@ -25,7 +25,7 @@ module ActiveRecord attributes = builder.build_from_database(foo: "3.3") assert_equal "3.3", attributes[:foo].value_before_type_cast - assert_equal nil, attributes[:bar].value_before_type_cast + assert_nil attributes[:bar].value_before_type_cast assert_equal :bar, attributes[:bar].name end diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb index 77ee3ca2d7..2203aa1788 100644 --- a/activerecord/test/cases/autosave_association_test.rb +++ b/activerecord/test/cases/autosave_association_test.rb @@ -36,11 +36,11 @@ class TestAutosaveAssociationsInGeneral < ActiveRecord::TestCase private - def should_be_cool - unless self.first_name == "cool" - errors.add :first_name, "not cool" + def should_be_cool + unless first_name == "cool" + errors.add :first_name, "not cool" + end end - end } reference = Class.new(ActiveRecord::Base) { self.table_name = "references" @@ -1391,6 +1391,14 @@ module AutosaveAssociationOnACollectionAssociationTests assert_equal "Squawky", parrot.reload.name end + def test_should_not_update_children_when_parent_creation_with_no_reason + parrot = Parrot.create!(name: "Polly") + assert_equal 0, parrot.updated_count + + Pirate.create!(parrot_ids: [parrot.id], catchphrase: "Arrrr") + assert_equal 0, parrot.reload.updated_count + end + def test_should_automatically_validate_the_associated_models @pirate.send(@association_name).each { |child| child.name = "" } diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 4e9d78de5d..a611cc208c 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -4,6 +4,7 @@ require "models/author" require "models/topic" require "models/reply" require "models/category" +require "models/categorization" require "models/company" require "models/customer" require "models/developer" @@ -33,8 +34,6 @@ class SecondAbstractClass < FirstAbstractClass self.abstract_class = true end class Photo < SecondAbstractClass; end -class Category < ActiveRecord::Base; end -class Categorization < ActiveRecord::Base; end class Smarts < ActiveRecord::Base; end class CreditCard < ActiveRecord::Base class PinNumber < ActiveRecord::Base @@ -45,8 +44,6 @@ class CreditCard < ActiveRecord::Base class Brand < Category; end end class MasterCreditCard < ActiveRecord::Base; end -class Post < ActiveRecord::Base; end -class Computer < ActiveRecord::Base; end class NonExistentTable < ActiveRecord::Base; end class TestOracleDefault < ActiveRecord::Base; end @@ -56,12 +53,6 @@ end class Weird < ActiveRecord::Base; end -class Boolean < ActiveRecord::Base - def has_fun - super - end -end - class LintTest < ActiveRecord::TestCase include ActiveModel::Lint::Tests @@ -107,14 +98,6 @@ class BasicsTest < ActiveRecord::TestCase assert_nil Edge.primary_key end - unless current_adapter?(:PostgreSQLAdapter, :OracleAdapter, :SQLServerAdapter, :FbAdapter) - def test_limit_with_comma - assert_deprecated do - assert Topic.limit("1,2").to_a - end - end - end - def test_many_mutations car = Car.new name: "<3<3<3" car.engines_count = 0 @@ -144,10 +127,8 @@ class BasicsTest < ActiveRecord::TestCase end def test_limit_should_sanitize_sql_injection_for_limit_with_commas - assert_deprecated do - assert_raises(ArgumentError) do - Topic.limit("1, 7 procedure help()").to_a - end + assert_raises(ArgumentError) do + Topic.limit("1, 7 procedure help()").to_a end end @@ -622,7 +603,7 @@ class BasicsTest < ActiveRecord::TestCase Topic.find(topic.id).destroy end - assert_equal nil, Topic.find_by_id(topic.id) + assert_nil Topic.find_by_id(topic.id) end def test_comparison_with_different_objects @@ -1379,12 +1360,6 @@ class BasicsTest < ActiveRecord::TestCase end end - def test_uniq_delegates_to_scoped - assert_deprecated do - assert_equal Bird.all.distinct, Bird.uniq - end - end - def test_distinct_delegates_to_scoped assert_equal Bird.all.distinct, Bird.distinct end diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb index 87e99fb25c..b29733a676 100644 --- a/activerecord/test/cases/calculations_test.rb +++ b/activerecord/test/cases/calculations_test.rb @@ -13,9 +13,9 @@ require "models/speedometer" require "models/ship_part" require "models/treasure" require "models/developer" +require "models/post" require "models/comment" require "models/rating" -require "models/post" class NumericData < ActiveRecord::Base self.table_name = "numeric_data" @@ -421,10 +421,6 @@ class CalculationsTest < ActiveRecord::TestCase def test_count_with_distinct assert_equal 4, Account.select(:credit_limit).distinct.count - - assert_deprecated do - assert_equal 4, Account.select(:credit_limit).uniq.count - end end def test_count_with_aliased_attribute diff --git a/activerecord/test/cases/callbacks_test.rb b/activerecord/test/cases/callbacks_test.rb index 4b517e9d70..53ff037de1 100644 --- a/activerecord/test/cases/callbacks_test.rb +++ b/activerecord/test/cases/callbacks_test.rb @@ -125,11 +125,11 @@ class ContextualCallbacksDeveloper < ActiveRecord::Base after_validation :after_validation_on_create_and_update, on: [ :create, :update ] def before_validation_on_create_and_update - history << "before_validation_on_#{self.validation_context}".to_sym + history << "before_validation_on_#{validation_context}".to_sym end def after_validation_on_create_and_update - history << "after_validation_on_#{self.validation_context}".to_sym + history << "after_validation_on_#{validation_context}".to_sym end def history @@ -449,7 +449,7 @@ class CallbacksTest < ActiveRecord::TestCase assert david.valid? assert !david.save exc = assert_raise(ActiveRecord::RecordNotSaved) { david.save! } - assert_equal exc.record, david + assert_equal david, exc.record assert_equal "Failed to save the record", exc.message end @@ -493,7 +493,7 @@ class CallbacksTest < ActiveRecord::TestCase assert_deprecated do assert !david.destroy exc = assert_raise(ActiveRecord::RecordNotDestroyed) { david.destroy! } - assert_equal exc.record, david + assert_equal david, exc.record assert_equal "Failed to destroy the record", exc.message end assert_not_nil ImmutableDeveloper.find_by_id(1) @@ -527,7 +527,7 @@ class CallbacksTest < ActiveRecord::TestCase assert david.valid? assert !david.save exc = assert_raise(ActiveRecord::RecordNotSaved) { david.save! } - assert_equal exc.record, david + assert_equal david, exc.record david = DeveloperWithCanceledCallbacks.find(1) david.salary = 10_000_000 @@ -554,7 +554,7 @@ class CallbacksTest < ActiveRecord::TestCase david = DeveloperWithCanceledCallbacks.find(1) assert !david.destroy exc = assert_raise(ActiveRecord::RecordNotDestroyed) { david.destroy! } - assert_equal exc.record, david + assert_equal david, exc.record assert_not_nil ImmutableDeveloper.find_by_id(1) someone = CallbackHaltedDeveloper.find(1) diff --git a/activerecord/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb b/activerecord/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb index a8955152c3..8faa67255d 100644 --- a/activerecord/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb +++ b/activerecord/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb @@ -142,13 +142,13 @@ module ActiveRecord "database" => "foo", "host" => "localhost" } assert_equal expected, actual["default_env"] - assert_equal nil, actual["production"] - assert_equal nil, actual["development"] - assert_equal nil, actual["test"] - assert_equal nil, actual[:default_env] - assert_equal nil, actual[:production] - assert_equal nil, actual[:development] - assert_equal nil, actual[:test] + assert_nil actual["production"] + assert_nil actual["development"] + assert_nil actual["test"] + assert_nil actual[:default_env] + assert_nil actual[:production] + assert_nil actual[:development] + assert_nil actual[:test] end def test_blank_with_database_url_with_rails_env @@ -162,15 +162,15 @@ module ActiveRecord "host" => "localhost" } assert_equal expected, actual["not_production"] - assert_equal nil, actual["production"] - assert_equal nil, actual["default_env"] - assert_equal nil, actual["development"] - assert_equal nil, actual["test"] - assert_equal nil, actual[:default_env] - assert_equal nil, actual[:not_production] - assert_equal nil, actual[:production] - assert_equal nil, actual[:development] - assert_equal nil, actual[:test] + assert_nil actual["production"] + assert_nil actual["default_env"] + assert_nil actual["development"] + assert_nil actual["test"] + assert_nil actual[:default_env] + assert_nil actual[:not_production] + assert_nil actual[:production] + assert_nil actual[:development] + assert_nil actual[:test] end def test_blank_with_database_url_with_rack_env @@ -184,15 +184,15 @@ module ActiveRecord "host" => "localhost" } assert_equal expected, actual["not_production"] - assert_equal nil, actual["production"] - assert_equal nil, actual["default_env"] - assert_equal nil, actual["development"] - assert_equal nil, actual["test"] - assert_equal nil, actual[:default_env] - assert_equal nil, actual[:not_production] - assert_equal nil, actual[:production] - assert_equal nil, actual[:development] - assert_equal nil, actual[:test] + assert_nil actual["production"] + assert_nil actual["default_env"] + assert_nil actual["development"] + assert_nil actual["test"] + assert_nil actual[:default_env] + assert_nil actual[:not_production] + assert_nil actual[:production] + assert_nil actual[:development] + assert_nil actual[:test] end def test_database_url_with_ipv6_host_and_port diff --git a/activerecord/test/cases/connection_adapters/schema_cache_test.rb b/activerecord/test/cases/connection_adapters/schema_cache_test.rb index 1b4f80fc67..106323ccc9 100644 --- a/activerecord/test/cases/connection_adapters/schema_cache_test.rb +++ b/activerecord/test/cases/connection_adapters/schema_cache_test.rb @@ -80,10 +80,13 @@ module ActiveRecord end end - def test_table_methods_deprecation - assert_deprecated { assert @cache.table_exists?("posts") } - assert_deprecated { assert @cache.tables("posts") } - assert_deprecated { @cache.clear_table_cache!("posts") } + def test_data_source_exist + assert @cache.data_source_exists?("posts") + assert_not @cache.data_source_exists?("foo") + end + + def test_clear_data_source_cache + @cache.clear_data_source_cache!("posts") end private diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb index 1d4cd3c78b..81d7113bc6 100644 --- a/activerecord/test/cases/connection_pool_test.rb +++ b/activerecord/test/cases/connection_pool_test.rb @@ -341,14 +341,18 @@ module ActiveRecord end end + class ConnectionTestModel < ActiveRecord::Base + end + def test_connection_notification_is_called payloads = [] subscription = ActiveSupport::Notifications.subscribe("!connection.active_record") do |name, started, finished, unique_id, payload| payloads << payload end - ActiveRecord::Base.establish_connection :arunit + ConnectionTestModel.establish_connection :arunit + assert_equal [:config, :connection_id, :spec_name], payloads[0].keys.sort - assert_equal "primary", payloads[0][:spec_name] + assert_equal "ActiveRecord::ConnectionAdapters::ConnectionPoolTest::ConnectionTestModel", payloads[0][:spec_name] ensure ActiveSupport::Notifications.unsubscribe(subscription) if subscription end @@ -501,11 +505,11 @@ module ActiveRecord first_thread.join(2) second_thread.join(2) - puts '---' + puts "---" p [first_thread, second_thread] p pool.stat p pool.connections.map(&:owner) - puts '<<<' + puts "<<<" puts end diff --git a/activerecord/test/cases/counter_cache_test.rb b/activerecord/test/cases/counter_cache_test.rb index 84f2c3a465..c7d0ba32b4 100644 --- a/activerecord/test/cases/counter_cache_test.rb +++ b/activerecord/test/cases/counter_cache_test.rb @@ -211,4 +211,146 @@ class CounterCacheTest < ActiveRecord::TestCase aircraft.wheels.first.destroy end end + + test "update counters doesn't touch timestamps by default" do + @topic.update_column :updated_at, 5.minutes.ago + previously_updated_at = @topic.updated_at + + Topic.update_counters(@topic.id, replies_count: -1) + + assert_equal previously_updated_at, @topic.updated_at + end + + test "update counters with touch: true" do + assert_touching @topic, :updated_at do + Topic.update_counters(@topic.id, replies_count: -1, touch: true) + end + end + + test "update counters of multiple records with touch: true" do + t1, t2 = topics(:first, :second) + + assert_touching t1, :updated_at do + assert_difference ["t1.reload.replies_count", "t2.reload.replies_count"], 2 do + Topic.update_counters([t1.id, t2.id], replies_count: 2, touch: true) + end + end + end + + test "update multiple counters with touch: true" do + assert_touching @topic, :updated_at do + Topic.update_counters(@topic.id, replies_count: 2, unique_replies_count: 2, touch: true) + end + end + + test "reset counters with touch: true" do + assert_touching @topic, :updated_at do + Topic.reset_counters(@topic.id, :replies, touch: true) + end + end + + test "reset multiple counters with touch: true" do + assert_touching @topic, :updated_at do + Topic.update_counters(@topic.id, replies_count: 1, unique_replies_count: 1) + Topic.reset_counters(@topic.id, :replies, :unique_replies, touch: true) + end + end + + test "increment counters with touch: true" do + assert_touching @topic, :updated_at do + Topic.increment_counter(:replies_count, @topic.id, touch: true) + end + end + + test "decrement counters with touch: true" do + assert_touching @topic, :updated_at do + Topic.decrement_counter(:replies_count, @topic.id, touch: true) + end + end + + test "update counters with touch: :written_on" do + assert_touching @topic, :written_on do + Topic.update_counters(@topic.id, replies_count: -1, touch: :written_on) + end + end + + test "update multiple counters with touch: :written_on" do + assert_touching @topic, :written_on do + Topic.update_counters(@topic.id, replies_count: 2, unique_replies_count: 2, touch: :written_on) + end + end + + test "reset counters with touch: :written_on" do + assert_touching @topic, :written_on do + Topic.reset_counters(@topic.id, :replies, touch: :written_on) + end + end + + test "reset multiple counters with touch: :written_on" do + assert_touching @topic, :written_on do + Topic.update_counters(@topic.id, replies_count: 1, unique_replies_count: 1) + Topic.reset_counters(@topic.id, :replies, :unique_replies, touch: :written_on) + end + end + + test "increment counters with touch: :written_on" do + assert_touching @topic, :written_on do + Topic.increment_counter(:replies_count, @topic.id, touch: :written_on) + end + end + + test "decrement counters with touch: :written_on" do + assert_touching @topic, :written_on do + Topic.decrement_counter(:replies_count, @topic.id, touch: :written_on) + end + end + + test "update counters with touch: %i( updated_at written_on )" do + assert_touching @topic, :updated_at, :written_on do + Topic.update_counters(@topic.id, replies_count: -1, touch: %i( updated_at written_on )) + end + end + + test "update multiple counters with touch: %i( updated_at written_on )" do + assert_touching @topic, :updated_at, :written_on do + Topic.update_counters(@topic.id, replies_count: 2, unique_replies_count: 2, touch: %i( updated_at written_on )) + end + end + + test "reset counters with touch: %i( updated_at written_on )" do + assert_touching @topic, :updated_at, :written_on do + Topic.reset_counters(@topic.id, :replies, touch: %i( updated_at written_on )) + end + end + + test "reset multiple counters with touch: %i( updated_at written_on )" do + assert_touching @topic, :updated_at, :written_on do + Topic.update_counters(@topic.id, replies_count: 1, unique_replies_count: 1) + Topic.reset_counters(@topic.id, :replies, :unique_replies, touch: %i( updated_at written_on )) + end + end + + test "increment counters with touch: %i( updated_at written_on )" do + assert_touching @topic, :updated_at, :written_on do + Topic.increment_counter(:replies_count, @topic.id, touch: %i( updated_at written_on )) + end + end + + test "decrement counters with touch: %i( updated_at written_on )" do + assert_touching @topic, :updated_at, :written_on do + Topic.decrement_counter(:replies_count, @topic.id, touch: %i( updated_at written_on )) + end + end + + private + def assert_touching(record, *attributes) + record.update_columns attributes.map { |attr| [ attr, 5.minutes.ago ] }.to_h + touch_times = attributes.map { |attr| [ attr, record.public_send(attr) ] }.to_h + + yield + + touch_times.each do |attr, previous_touch_time| + assert_operator previous_touch_time, :<, record.reload.public_send(attr) + end + end end diff --git a/activerecord/test/cases/database_statements_test.rb b/activerecord/test/cases/database_statements_test.rb index bb16076fd2..66035865be 100644 --- a/activerecord/test/cases/database_statements_test.rb +++ b/activerecord/test/cases/database_statements_test.rb @@ -20,12 +20,6 @@ class DatabaseStatementsTest < ActiveRecord::TestCase assert_not_nil return_the_inserted_id(method: :create) end - def test_insert_update_delete_sql_is_deprecated - assert_deprecated { @connection.insert_sql("INSERT INTO accounts (firm_id,credit_limit) VALUES (42,5000)") } - assert_deprecated { @connection.update_sql("UPDATE accounts SET credit_limit = 6000 WHERE firm_id = 42") } - assert_deprecated { @connection.delete_sql("DELETE FROM accounts WHERE firm_id = 42") } - end - private def return_the_inserted_id(method:) diff --git a/activerecord/test/cases/errors_test.rb b/activerecord/test/cases/errors_test.rb index 0711a372f2..73feb831d0 100644 --- a/activerecord/test/cases/errors_test.rb +++ b/activerecord/test/cases/errors_test.rb @@ -5,7 +5,7 @@ class ErrorsTest < ActiveRecord::TestCase base = ActiveRecord::ActiveRecordError error_klasses = ObjectSpace.each_object(Class).select { |klass| klass < base } - error_klasses.each do |error_klass| + (error_klasses - [ActiveRecord::AmbiguousSourceReflectionForThroughAssociation]).each do |error_klass| begin error_klass.new.inspect rescue ArgumentError diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index f8724b0993..e0ad9f5ec1 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -117,8 +117,8 @@ class FinderTest < ActiveRecord::TestCase assert_equal "The Fourth Topic of the day", records[2].title end - def test_find_passing_active_record_object_is_deprecated - assert_deprecated do + def test_find_passing_active_record_object_is_not_permitted + assert_raises(ArgumentError) do Topic.find(Topic.last) end end @@ -167,8 +167,8 @@ class FinderTest < ActiveRecord::TestCase assert_equal false, relation.exists?(false) end - def test_exists_passing_active_record_object_is_deprecated - assert_deprecated do + def test_exists_passing_active_record_object_is_not_permitted + assert_raises(ArgumentError) do Topic.exists?(Topic.new) end end @@ -339,6 +339,11 @@ class FinderTest < ActiveRecord::TestCase assert_equal author.post, Post.find_by(author_id: Author.where(id: author)) end + def test_find_by_and_where_consistency_with_active_record_instance + author = authors(:david) + assert_equal Post.where(author_id: author).take, Post.find_by(author_id: author) + end + def test_take assert_equal topics(:first), Topic.take end @@ -488,12 +493,12 @@ class FinderTest < ActiveRecord::TestCase assert_equal topics(:fourth), Topic.offset(1).second_to_last assert_equal topics(:fourth), Topic.offset(2).second_to_last assert_equal topics(:fourth), Topic.offset(3).second_to_last - assert_equal nil, Topic.offset(4).second_to_last - assert_equal nil, Topic.offset(5).second_to_last + assert_nil Topic.offset(4).second_to_last + assert_nil Topic.offset(5).second_to_last #test with limit - # assert_equal nil, Topic.limit(1).second # TODO: currently failing - assert_equal nil, Topic.limit(1).second_to_last + # assert_nil Topic.limit(1).second # TODO: currently failing + assert_nil Topic.limit(1).second_to_last end def test_second_to_last_have_primary_key_order_by_default @@ -516,15 +521,15 @@ class FinderTest < ActiveRecord::TestCase # test with offset assert_equal topics(:third), Topic.offset(1).third_to_last assert_equal topics(:third), Topic.offset(2).third_to_last - assert_equal nil, Topic.offset(3).third_to_last - assert_equal nil, Topic.offset(4).third_to_last - assert_equal nil, Topic.offset(5).third_to_last + assert_nil Topic.offset(3).third_to_last + assert_nil Topic.offset(4).third_to_last + assert_nil Topic.offset(5).third_to_last # test with limit - # assert_equal nil, Topic.limit(1).third # TODO: currently failing - assert_equal nil, Topic.limit(1).third_to_last - # assert_equal nil, Topic.limit(2).third # TODO: currently failing - assert_equal nil, Topic.limit(2).third_to_last + # assert_nil Topic.limit(1).third # TODO: currently failing + assert_nil Topic.limit(1).third_to_last + # assert_nil Topic.limit(2).third # TODO: currently failing + assert_nil Topic.limit(2).third_to_last end def test_third_to_last_have_primary_key_order_by_default @@ -592,7 +597,7 @@ class FinderTest < ActiveRecord::TestCase end def test_last_with_irreversible_order - assert_deprecated do + assert_raises(ActiveRecord::IrreversibleOrderError) do Topic.order("coalesce(author_name, title)").last end end @@ -1166,7 +1171,7 @@ class FinderTest < ActiveRecord::TestCase end test "find_by returns nil if the record is missing" do - assert_equal nil, Post.find_by("1 = 0") + assert_nil Post.find_by("1 = 0") end test "find_by with associations" do @@ -1220,7 +1225,7 @@ class FinderTest < ActiveRecord::TestCase assert_equal tyre2, zyke.tyres.custom_find_by(id: tyre2.id) end - protected + private def table_with_custom_primary_key yield(Class.new(Toy) do def self.name diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb index f3d0e4a1b1..61e596e208 100644 --- a/activerecord/test/cases/fixtures_test.rb +++ b/activerecord/test/cases/fixtures_test.rb @@ -7,17 +7,19 @@ require "models/binary" require "models/book" require "models/bulb" require "models/category" +require "models/post" require "models/comment" require "models/company" require "models/computer" require "models/course" require "models/developer" +require "models/dog" require "models/doubloon" require "models/joke" require "models/matey" +require "models/other_dog" require "models/parrot" require "models/pirate" -require "models/post" require "models/randomly_named_c1" require "models/reply" require "models/ship" @@ -211,9 +213,19 @@ class FixturesTest < ActiveRecord::TestCase end def test_dirty_dirty_yaml_file - assert_raise(ActiveRecord::Fixture::FormatError) do - ActiveRecord::FixtureSet.new(Account.connection, "courses", Course, FIXTURES_ROOT + "/naked/yml/courses") + fixture_path = FIXTURES_ROOT + "/naked/yml/courses" + error = assert_raise(ActiveRecord::Fixture::FormatError) do + ActiveRecord::FixtureSet.new(Account.connection, "courses", Course, fixture_path) + end + assert_equal "fixture is not a hash: #{fixture_path}.yml", error.to_s + end + + def test_yaml_file_with_one_invalid_fixture + fixture_path = FIXTURES_ROOT + "/naked/yml/courses_with_invalid_key" + error = assert_raise(ActiveRecord::Fixture::FormatError) do + ActiveRecord::FixtureSet.new(Account.connection, "courses", Course, fixture_path) end + assert_equal "fixture key is not a hash: #{fixture_path}.yml, keys: [\"two\"]", error.to_s end def test_yaml_file_with_invalid_column @@ -1000,7 +1012,7 @@ end class FixtureClassNamesTest < ActiveRecord::TestCase def setup - @saved_cache = self.fixture_class_names.dup + @saved_cache = fixture_class_names.dup end def teardown @@ -1011,3 +1023,16 @@ class FixtureClassNamesTest < ActiveRecord::TestCase assert_nil fixture_class_names["unregistered_identifier"] end end + +class SameNameDifferentDatabaseFixturesTest < ActiveRecord::TestCase + fixtures :dogs, :other_dogs + + test "fixtures are properly loaded" do + # Force loading the fixtures again to reproduce issue + ActiveRecord::FixtureSet.reset_cache + create_fixtures("dogs", "other_dogs") + + assert_kind_of Dog, dogs(:sophie) + assert_kind_of OtherDog, other_dogs(:lassie) + end +end diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb index f1d69a215a..1ddcbf0e4f 100644 --- a/activerecord/test/cases/helper.rb +++ b/activerecord/test/cases/helper.rb @@ -27,9 +27,6 @@ ARTest.connect # Quote "type" if it's a reserved word for the current connection. QUOTED_TYPE = ActiveRecord::Base.connection.quote_column_name("type") -# FIXME: Remove this when the deprecation cycle on TZ aware types by default ends. -ActiveRecord::Base.time_zone_aware_types << :time - def current_adapter?(*types) types.any? do |type| ActiveRecord::ConnectionAdapters.const_defined?(type) && diff --git a/activerecord/test/cases/inheritance_test.rb b/activerecord/test/cases/inheritance_test.rb index 9ad4664567..e570e9ac1d 100644 --- a/activerecord/test/cases/inheritance_test.rb +++ b/activerecord/test/cases/inheritance_test.rb @@ -58,21 +58,21 @@ class InheritanceTest < ActiveRecord::TestCase end def test_compute_type_success - assert_equal Author, ActiveRecord::Base.send(:compute_type, "Author") + assert_equal Author, Company.send(:compute_type, "Author") end def test_compute_type_nonexistent_constant e = assert_raises NameError do - ActiveRecord::Base.send :compute_type, "NonexistentModel" + Company.send :compute_type, "NonexistentModel" end - assert_equal "uninitialized constant ActiveRecord::Base::NonexistentModel", e.message - assert_equal "ActiveRecord::Base::NonexistentModel", e.name + assert_equal "uninitialized constant Company::NonexistentModel", e.message + assert_equal "Company::NonexistentModel", e.name end def test_compute_type_no_method_error ActiveSupport::Dependencies.stub(:safe_constantize, proc { raise NoMethodError }) do assert_raises NoMethodError do - ActiveRecord::Base.send :compute_type, "InvalidModel" + Company.send :compute_type, "InvalidModel" end end end @@ -90,7 +90,7 @@ class InheritanceTest < ActiveRecord::TestCase ActiveSupport::Dependencies.stub(:safe_constantize, proc { raise e }) do exception = assert_raises NameError do - ActiveRecord::Base.send :compute_type, "InvalidModel" + Company.send :compute_type, "InvalidModel" end assert_equal error.message, exception.message end @@ -99,7 +99,7 @@ class InheritanceTest < ActiveRecord::TestCase def test_compute_type_argument_error ActiveSupport::Dependencies.stub(:safe_constantize, proc { raise ArgumentError }) do assert_raises ArgumentError do - ActiveRecord::Base.send :compute_type, "InvalidModel" + Company.send :compute_type, "InvalidModel" end end end diff --git a/activerecord/test/cases/integration_test.rb b/activerecord/test/cases/integration_test.rb index 00457965d7..d7aa091623 100644 --- a/activerecord/test/cases/integration_test.rb +++ b/activerecord/test/cases/integration_test.rb @@ -15,7 +15,7 @@ class IntegrationTest < ActiveRecord::TestCase def test_to_param_returns_nil_if_not_persisted client = Client.new - assert_equal nil, client.to_param + assert_nil client.to_param end def test_to_param_returns_id_if_not_persisted_but_id_is_set @@ -89,7 +89,7 @@ class IntegrationTest < ActiveRecord::TestCase def test_to_param_class_method_uses_default_if_not_persisted firm = Firm.new(name: "Fancy Shirts") - assert_equal nil, firm.to_param + assert_nil firm.to_param end def test_to_param_with_no_arguments diff --git a/activerecord/test/cases/invertible_migration_test.rb b/activerecord/test/cases/invertible_migration_test.rb index 9d5aace7db..cc3951e2ba 100644 --- a/activerecord/test/cases/invertible_migration_test.rb +++ b/activerecord/test/cases/invertible_migration_test.rb @@ -165,10 +165,8 @@ module ActiveRecord teardown do %w[horses new_horses].each do |table| - ActiveSupport::Deprecation.silence do - if ActiveRecord::Base.connection.table_exists?(table) - ActiveRecord::Base.connection.drop_table(table) - end + if ActiveRecord::Base.connection.table_exists?(table) + ActiveRecord::Base.connection.drop_table(table) end end ActiveRecord::Migration.verbose = @verbose_was @@ -199,14 +197,14 @@ module ActiveRecord def test_migrate_up migration = InvertibleMigration.new migration.migrate(:up) - ActiveSupport::Deprecation.silence { assert migration.connection.table_exists?("horses"), "horses should exist" } + assert migration.connection.table_exists?("horses"), "horses should exist" end def test_migrate_down migration = InvertibleMigration.new migration.migrate :up migration.migrate :down - ActiveSupport::Deprecation.silence { assert !migration.connection.table_exists?("horses") } + assert !migration.connection.table_exists?("horses") end def test_migrate_revert @@ -214,11 +212,11 @@ module ActiveRecord revert = InvertibleRevertMigration.new migration.migrate :up revert.migrate :up - ActiveSupport::Deprecation.silence { assert !migration.connection.table_exists?("horses") } + assert !migration.connection.table_exists?("horses") revert.migrate :down - ActiveSupport::Deprecation.silence { assert migration.connection.table_exists?("horses") } + assert migration.connection.table_exists?("horses") migration.migrate :down - ActiveSupport::Deprecation.silence { assert !migration.connection.table_exists?("horses") } + assert !migration.connection.table_exists?("horses") end def test_migrate_revert_by_part @@ -226,24 +224,18 @@ module ActiveRecord received = [] migration = InvertibleByPartsMigration.new migration.test = ->(dir) { - ActiveSupport::Deprecation.silence do - assert migration.connection.table_exists?("horses") - assert migration.connection.table_exists?("new_horses") - end + assert migration.connection.table_exists?("horses") + assert migration.connection.table_exists?("new_horses") received << dir } migration.migrate :up assert_equal [:both, :up], received - ActiveSupport::Deprecation.silence do - assert !migration.connection.table_exists?("horses") - assert migration.connection.table_exists?("new_horses") - end + assert !migration.connection.table_exists?("horses") + assert migration.connection.table_exists?("new_horses") migration.migrate :down assert_equal [:both, :up, :both, :down], received - ActiveSupport::Deprecation.silence do - assert migration.connection.table_exists?("horses") - assert !migration.connection.table_exists?("new_horses") - end + assert migration.connection.table_exists?("horses") + assert !migration.connection.table_exists?("new_horses") end def test_migrate_revert_whole_migration @@ -252,20 +244,20 @@ module ActiveRecord revert = RevertWholeMigration.new(klass) migration.migrate :up revert.migrate :up - ActiveSupport::Deprecation.silence { assert !migration.connection.table_exists?("horses") } + assert !migration.connection.table_exists?("horses") revert.migrate :down - ActiveSupport::Deprecation.silence { assert migration.connection.table_exists?("horses") } + assert migration.connection.table_exists?("horses") migration.migrate :down - ActiveSupport::Deprecation.silence { assert !migration.connection.table_exists?("horses") } + assert !migration.connection.table_exists?("horses") end end def test_migrate_nested_revert_whole_migration revert = NestedRevertWholeMigration.new(InvertibleRevertMigration) revert.migrate :down - ActiveSupport::Deprecation.silence { assert revert.connection.table_exists?("horses") } + assert revert.connection.table_exists?("horses") revert.migrate :up - ActiveSupport::Deprecation.silence { assert !revert.connection.table_exists?("horses") } + assert !revert.connection.table_exists?("horses") end def test_migrate_revert_change_column_default @@ -330,24 +322,24 @@ module ActiveRecord def test_legacy_up LegacyMigration.migrate :up - ActiveSupport::Deprecation.silence { assert ActiveRecord::Base.connection.table_exists?("horses"), "horses should exist" } + assert ActiveRecord::Base.connection.table_exists?("horses"), "horses should exist" end def test_legacy_down LegacyMigration.migrate :up LegacyMigration.migrate :down - ActiveSupport::Deprecation.silence { assert !ActiveRecord::Base.connection.table_exists?("horses"), "horses should not exist" } + assert !ActiveRecord::Base.connection.table_exists?("horses"), "horses should not exist" end def test_up LegacyMigration.up - ActiveSupport::Deprecation.silence { assert ActiveRecord::Base.connection.table_exists?("horses"), "horses should exist" } + assert ActiveRecord::Base.connection.table_exists?("horses"), "horses should exist" end def test_down LegacyMigration.up LegacyMigration.down - ActiveSupport::Deprecation.silence { assert !ActiveRecord::Base.connection.table_exists?("horses"), "horses should not exist" } + assert !ActiveRecord::Base.connection.table_exists?("horses"), "horses should not exist" end def test_migrate_down_with_table_name_prefix @@ -356,7 +348,7 @@ module ActiveRecord migration = InvertibleMigration.new migration.migrate(:up) assert_nothing_raised { migration.migrate(:down) } - ActiveSupport::Deprecation.silence { assert !ActiveRecord::Base.connection.table_exists?("p_horses_s"), "p_horses_s should not exist" } + assert !ActiveRecord::Base.connection.table_exists?("p_horses_s"), "p_horses_s should not exist" ensure ActiveRecord::Base.table_name_prefix = ActiveRecord::Base.table_name_suffix = "" end diff --git a/activerecord/test/cases/json_serialization_test.rb b/activerecord/test/cases/json_serialization_test.rb index a2150483f3..155e858822 100644 --- a/activerecord/test/cases/json_serialization_test.rb +++ b/activerecord/test/cases/json_serialization_test.rb @@ -243,7 +243,7 @@ class DatabaseConnectedJsonEncodingTest < ActiveRecord::TestCase assert !@david.posts.first.respond_to?(:favorite_quote) assert_match %r{"favorite_quote":"Constraints are liberating"}, json - assert_equal %r{"favorite_quote":}.match(json).size, 1 + assert_equal 1, %r{"favorite_quote":}.match(json).size end def test_should_allow_only_option_for_list_of_authors diff --git a/activerecord/test/cases/locking_test.rb b/activerecord/test/cases/locking_test.rb index 9b0ec54aa7..9e42242e55 100644 --- a/activerecord/test/cases/locking_test.rb +++ b/activerecord/test/cases/locking_test.rb @@ -18,7 +18,7 @@ class LockWithoutDefault < ActiveRecord::Base; end class LockWithCustomColumnWithoutDefault < ActiveRecord::Base self.table_name = :lock_without_defaults_cust - self.column_defaults # to test @column_defaults caching. + column_defaults # to test @column_defaults caching. self.locking_column = :custom_lock_version end @@ -579,7 +579,7 @@ unless in_memory_db? assert first.end > second.end end - protected + private def duel(zzz = 5) t0, t1, t2, t3 = nil, nil, nil, nil diff --git a/activerecord/test/cases/migration/change_schema_test.rb b/activerecord/test/cases/migration/change_schema_test.rb index 03f9c4a9ed..48cfe89882 100644 --- a/activerecord/test/cases/migration/change_schema_test.rb +++ b/activerecord/test/cases/migration/change_schema_test.rb @@ -409,9 +409,9 @@ module ActiveRecord def test_drop_table_if_exists connection.create_table(:testings) - ActiveSupport::Deprecation.silence { assert connection.table_exists?(:testings) } + assert connection.table_exists?(:testings) connection.drop_table(:testings, if_exists: true) - ActiveSupport::Deprecation.silence { assert_not connection.table_exists?(:testings) } + assert_not connection.table_exists?(:testings) end def test_drop_table_if_exists_nothing_raised diff --git a/activerecord/test/cases/migration/compatibility_test.rb b/activerecord/test/cases/migration/compatibility_test.rb index 0a4b604601..e5a7412bc3 100644 --- a/activerecord/test/cases/migration/compatibility_test.rb +++ b/activerecord/test/cases/migration/compatibility_test.rb @@ -55,7 +55,7 @@ module ActiveRecord end def test_references_does_not_add_index_by_default - migration = Class.new(ActiveRecord::Migration) { + migration = Class.new(ActiveRecord::Migration[4.2]) { def migrate(x) create_table :more_testings do |t| t.references :foo @@ -73,7 +73,7 @@ module ActiveRecord end def test_timestamps_have_null_constraints_if_not_present_in_migration_of_create_table - migration = Class.new(ActiveRecord::Migration) { + migration = Class.new(ActiveRecord::Migration[4.2]) { def migrate(x) create_table :more_testings do |t| t.timestamps @@ -90,7 +90,7 @@ module ActiveRecord end def test_timestamps_have_null_constraints_if_not_present_in_migration_for_adding_timestamps_to_existing_table - migration = Class.new(ActiveRecord::Migration) { + migration = Class.new(ActiveRecord::Migration[4.2]) { def migrate(x) add_timestamps :testings end @@ -102,15 +102,9 @@ module ActiveRecord assert connection.columns(:testings).find { |c| c.name == "updated_at" }.null end - def test_legacy_migrations_get_deprecation_warning_when_run - migration = Class.new(ActiveRecord::Migration) { - def up - add_column :testings, :baz, :string - end - } - - assert_deprecated do - migration.migrate :up + def test_legacy_migrations_raises_exception_when_inherited + assert_raises(StandardError) do + Class.new(ActiveRecord::Migration) end end end diff --git a/activerecord/test/cases/migration/create_join_table_test.rb b/activerecord/test/cases/migration/create_join_table_test.rb index f14d68f12b..26b1bb4419 100644 --- a/activerecord/test/cases/migration/create_join_table_test.rb +++ b/activerecord/test/cases/migration/create_join_table_test.rb @@ -12,9 +12,7 @@ module ActiveRecord teardown do %w(artists_musics musics_videos catalog).each do |table_name| - ActiveSupport::Deprecation.silence do - connection.drop_table table_name if connection.table_exists?(table_name) - end + connection.drop_table table_name if connection.table_exists?(table_name) end end @@ -84,51 +82,51 @@ module ActiveRecord connection.create_join_table :artists, :musics connection.drop_join_table :artists, :musics - ActiveSupport::Deprecation.silence { assert !connection.table_exists?("artists_musics") } + assert !connection.table_exists?("artists_musics") end def test_drop_join_table_with_strings connection.create_join_table :artists, :musics connection.drop_join_table "artists", "musics" - ActiveSupport::Deprecation.silence { assert !connection.table_exists?("artists_musics") } + assert !connection.table_exists?("artists_musics") end def test_drop_join_table_with_the_proper_order connection.create_join_table :videos, :musics connection.drop_join_table :videos, :musics - ActiveSupport::Deprecation.silence { assert !connection.table_exists?("musics_videos") } + assert !connection.table_exists?("musics_videos") end def test_drop_join_table_with_the_table_name connection.create_join_table :artists, :musics, table_name: :catalog connection.drop_join_table :artists, :musics, table_name: :catalog - ActiveSupport::Deprecation.silence { assert !connection.table_exists?("catalog") } + assert !connection.table_exists?("catalog") end def test_drop_join_table_with_the_table_name_as_string connection.create_join_table :artists, :musics, table_name: "catalog" connection.drop_join_table :artists, :musics, table_name: "catalog" - ActiveSupport::Deprecation.silence { assert !connection.table_exists?("catalog") } + assert !connection.table_exists?("catalog") end def test_drop_join_table_with_column_options connection.create_join_table :artists, :musics, column_options: { null: true } connection.drop_join_table :artists, :musics, column_options: { null: true } - ActiveSupport::Deprecation.silence { assert !connection.table_exists?("artists_musics") } + assert !connection.table_exists?("artists_musics") end def test_create_and_drop_join_table_with_common_prefix with_table_cleanup do connection.create_join_table "audio_artists", "audio_musics" - ActiveSupport::Deprecation.silence { assert connection.table_exists?("audio_artists_musics") } + assert connection.table_exists?("audio_artists_musics") connection.drop_join_table "audio_artists", "audio_musics" - ActiveSupport::Deprecation.silence { assert !connection.table_exists?("audio_artists_musics"), "Should have dropped join table, but didn't" } + assert !connection.table_exists?("audio_artists_musics"), "Should have dropped join table, but didn't" end end diff --git a/activerecord/test/cases/migration/foreign_key_test.rb b/activerecord/test/cases/migration/foreign_key_test.rb index 1921a4d7c2..9be6667aa1 100644 --- a/activerecord/test/cases/migration/foreign_key_test.rb +++ b/activerecord/test/cases/migration/foreign_key_test.rb @@ -101,7 +101,7 @@ if ActiveRecord::Base.connection.supports_foreign_keys? fk = foreign_keys.first if current_adapter?(:Mysql2Adapter) # ON DELETE RESTRICT is the default on MySQL - assert_equal nil, fk.on_delete + assert_nil fk.on_delete else assert_equal :restrict, fk.on_delete end diff --git a/activerecord/test/cases/migration/rename_table_test.rb b/activerecord/test/cases/migration/rename_table_test.rb index fc4f700916..19588d28a2 100644 --- a/activerecord/test/cases/migration/rename_table_test.rb +++ b/activerecord/test/cases/migration/rename_table_test.rb @@ -15,7 +15,7 @@ module ActiveRecord end def teardown - ActiveSupport::Deprecation.silence { rename_table :octopi, :test_models if connection.table_exists? :octopi } + rename_table :octopi, :test_models if connection.table_exists? :octopi super end @@ -82,7 +82,7 @@ module ActiveRecord def test_renaming_table_doesnt_attempt_to_rename_non_existent_sequences connection.create_table :cats, id: :uuid assert_nothing_raised { rename_table :cats, :felines } - ActiveSupport::Deprecation.silence { assert connection.table_exists? :felines } + assert connection.table_exists? :felines ensure connection.drop_table :cats, if_exists: true connection.drop_table :felines, if_exists: true diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index 22484ad678..4edb807bbb 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -399,6 +399,7 @@ class MigrationTest < ActiveRecord::TestCase ActiveRecord::Migrator.migrations_paths = old_path ENV["RAILS_ENV"] = original_rails_env ENV["RACK_ENV"] = original_rack_env + ActiveRecord::Migrator.up(migrations_path) end def test_migration_sets_internal_metadata_even_when_fully_migrated @@ -425,6 +426,7 @@ class MigrationTest < ActiveRecord::TestCase ActiveRecord::Migrator.migrations_paths = old_path ENV["RAILS_ENV"] = original_rails_env ENV["RACK_ENV"] = original_rack_env + ActiveRecord::Migrator.up(migrations_path) end def test_internal_metadata_stores_environment_when_other_data_exists @@ -705,7 +707,7 @@ class MigrationTest < ActiveRecord::TestCase end end - protected + private # This is needed to isolate class_attribute assignments like `table_name_prefix` # for each test case. def new_isolated_reminder_class @@ -887,7 +889,7 @@ if ActiveRecord::Base.connection.supports_bulk_alter? assert_equal :datetime, column(:birthdate).type end - protected + private def with_bulk_change_table # Reset columns/indexes cache as we're changing the table diff --git a/activerecord/test/cases/migrator_test.rb b/activerecord/test/cases/migrator_test.rb index b775bf0492..224e8d1854 100644 --- a/activerecord/test/cases/migrator_test.rb +++ b/activerecord/test/cases/migrator_test.rb @@ -290,6 +290,27 @@ class MigratorTest < ActiveRecord::TestCase assert_equal [[:up, 1], [:up, 2], [:up, 3]], calls end + def test_migrator_output_when_running_multiple_migrations + _, migrator = migrator_class(3) + + result = migrator.migrate("valid") + assert_equal(3, result.count) + + # Nothing migrated from duplicate run + result = migrator.migrate("valid") + assert_equal(0, result.count) + + result = migrator.rollback("valid") + assert_equal(1, result.count) + end + + def test_migrator_output_when_running_single_migration + _, migrator = migrator_class(1) + result = migrator.run(:up, "valid", 1) + + assert_equal(1, result.version) + end + def test_migrator_rollback _, migrator = migrator_class(3) @@ -313,9 +334,9 @@ class MigratorTest < ActiveRecord::TestCase _, migrator = migrator_class(3) ActiveRecord::Base.connection.drop_table "schema_migrations", if_exists: true - ActiveSupport::Deprecation.silence { assert_not ActiveRecord::Base.connection.table_exists?("schema_migrations") } + assert_not ActiveRecord::Base.connection.table_exists?("schema_migrations") migrator.migrate("valid", 1) - ActiveSupport::Deprecation.silence { assert ActiveRecord::Base.connection.table_exists?("schema_migrations") } + assert ActiveRecord::Base.connection.table_exists?("schema_migrations") end def test_migrator_forward diff --git a/activerecord/test/cases/primary_keys_test.rb b/activerecord/test/cases/primary_keys_test.rb index b434f4a6b6..1d72899102 100644 --- a/activerecord/test/cases/primary_keys_test.rb +++ b/activerecord/test/cases/primary_keys_test.rb @@ -129,12 +129,6 @@ class PrimaryKeysTest < ActiveRecord::TestCase assert_nothing_raised { MixedCaseMonkey.find(1).destroy } end - def test_supports_primary_key - assert_nothing_raised do - ActiveRecord::Base.connection.supports_primary_key? - end - end - if ActiveRecord::Base.connection.supports_primary_key? def test_primary_key_returns_value_if_it_exists klass = Class.new(ActiveRecord::Base) do diff --git a/activerecord/test/cases/query_cache_test.rb b/activerecord/test/cases/query_cache_test.rb index 4a49bfe9b1..93a67d0738 100644 --- a/activerecord/test/cases/query_cache_test.rb +++ b/activerecord/test/cases/query_cache_test.rb @@ -50,32 +50,36 @@ class QueryCacheTest < ActiveRecord::TestCase assert_cache :off end - def test_query_cache_across_threads - ActiveRecord::Base.connection_pool.connections.each do |conn| - assert_cache :off, conn - end - - assert !ActiveRecord::Base.connection.nil? - assert_cache :off - - middleware { - assert_cache :clean + private def with_temporary_connection_pool + old_pool = ActiveRecord::Base.connection_handler.retrieve_connection_pool(ActiveRecord::Base.connection_specification_name) + new_pool = ActiveRecord::ConnectionAdapters::ConnectionPool.new ActiveRecord::Base.connection_pool.spec + ActiveRecord::Base.connection_handler.send(:owner_to_pool)["primary"] = new_pool - Task.find 1 - assert_cache :dirty - - thread_1_connection = ActiveRecord::Base.connection - ActiveRecord::Base.clear_active_connections! - assert_cache :off, thread_1_connection + yield + ensure + ActiveRecord::Base.connection_handler.send(:owner_to_pool)["primary"] = old_pool + end - started = Concurrent::Event.new - checked = Concurrent::Event.new + def test_query_cache_across_threads + with_temporary_connection_pool do + begin + if in_memory_db? + # Separate connections to an in-memory database create an entirely new database, + # with an empty schema etc, so we just stub out this schema on the fly. + ActiveRecord::Base.connection_pool.with_connection do |connection| + connection.create_table :tasks do |t| + t.datetime :starting + t.datetime :ending + end + end + ActiveRecord::FixtureSet.create_fixtures(self.class.fixture_path, ["tasks"], {}, ActiveRecord::Base) + end - thread_2_connection = nil - thread = Thread.new { - thread_2_connection = ActiveRecord::Base.connection + ActiveRecord::Base.connection_pool.connections.each do |conn| + assert_cache :off, conn + end - assert_equal thread_2_connection, thread_1_connection + assert !ActiveRecord::Base.connection.nil? assert_cache :off middleware { @@ -84,29 +88,51 @@ class QueryCacheTest < ActiveRecord::TestCase Task.find 1 assert_cache :dirty - started.set - checked.wait - + thread_1_connection = ActiveRecord::Base.connection ActiveRecord::Base.clear_active_connections! - }.call({}) - } + assert_cache :off, thread_1_connection + + started = Concurrent::Event.new + checked = Concurrent::Event.new + + thread_2_connection = nil + thread = Thread.new { + thread_2_connection = ActiveRecord::Base.connection - started.wait + assert_equal thread_2_connection, thread_1_connection + assert_cache :off - thread_1_connection = ActiveRecord::Base.connection - assert_not_equal thread_1_connection, thread_2_connection - assert_cache :dirty, thread_2_connection - checked.set - thread.join + middleware { + assert_cache :clean - assert_cache :off, thread_2_connection - }.call({}) + Task.find 1 + assert_cache :dirty - ActiveRecord::Base.connection_pool.connections.each do |conn| - assert_cache :off, conn + started.set + checked.wait + + ActiveRecord::Base.clear_active_connections! + }.call({}) + } + + started.wait + + thread_1_connection = ActiveRecord::Base.connection + assert_not_equal thread_1_connection, thread_2_connection + assert_cache :dirty, thread_2_connection + checked.set + thread.join + + assert_cache :off, thread_2_connection + }.call({}) + + ActiveRecord::Base.connection_pool.connections.each do |conn| + assert_cache :off, conn + end + ensure + ActiveRecord::Base.clear_all_connections! + end end - ensure - ActiveRecord::Base.clear_all_connections! end def test_middleware_delegates @@ -275,6 +301,27 @@ class QueryCacheTest < ActiveRecord::TestCase Task.connection_specification_name = spec_name end + def test_query_cache_executes_new_queries_within_block + ActiveRecord::Base.connection.enable_query_cache! + + # Warm up the cache by running the query + assert_queries(1) do + assert_equal 0, Post.where(title: "test").to_a.count + end + + # Check that if the same query is run again, no queries are executed + assert_queries(0) do + assert_equal 0, Post.where(title: "test").to_a.count + end + + ActiveRecord::Base.connection.uncached do + # Check that new query is executed, avoiding the cache + assert_queries(1) do + assert_equal 0, Post.where(title: "test").to_a.count + end + end + end + def test_query_cache_doesnt_leak_cached_results_of_rolled_back_queries ActiveRecord::Base.connection.enable_query_cache! post = Post.first @@ -328,37 +375,44 @@ class QueryCacheTest < ActiveRecord::TestCase end def test_query_cache_does_not_establish_connection_if_unconnected - ActiveRecord::Base.clear_active_connections! - refute ActiveRecord::Base.connection_handler.active_connections? # sanity check + with_temporary_connection_pool do + ActiveRecord::Base.clear_active_connections! + refute ActiveRecord::Base.connection_handler.active_connections? # sanity check - middleware { - refute ActiveRecord::Base.connection_handler.active_connections?, "QueryCache forced ActiveRecord::Base to establish a connection in setup" - }.call({}) + middleware { + refute ActiveRecord::Base.connection_handler.active_connections?, "QueryCache forced ActiveRecord::Base to establish a connection in setup" + }.call({}) - refute ActiveRecord::Base.connection_handler.active_connections?, "QueryCache forced ActiveRecord::Base to establish a connection in cleanup" + refute ActiveRecord::Base.connection_handler.active_connections?, "QueryCache forced ActiveRecord::Base to establish a connection in cleanup" + end end def test_query_cache_is_enabled_on_connections_established_after_middleware_runs - ActiveRecord::Base.clear_active_connections! - refute ActiveRecord::Base.connection_handler.active_connections? # sanity check + with_temporary_connection_pool do + ActiveRecord::Base.clear_active_connections! + refute ActiveRecord::Base.connection_handler.active_connections? # sanity check - middleware { - assert ActiveRecord::Base.connection.query_cache_enabled, "QueryCache did not get lazily enabled" - }.call({}) + middleware { + assert ActiveRecord::Base.connection.query_cache_enabled, "QueryCache did not get lazily enabled" + }.call({}) + end end def test_query_caching_is_local_to_the_current_thread - ActiveRecord::Base.clear_active_connections! + with_temporary_connection_pool do + ActiveRecord::Base.clear_active_connections! - middleware { - assert ActiveRecord::Base.connection_pool.query_cache_enabled - assert ActiveRecord::Base.connection.query_cache_enabled + middleware { + assert ActiveRecord::Base.connection_pool.query_cache_enabled + assert ActiveRecord::Base.connection.query_cache_enabled - Thread.new { - refute ActiveRecord::Base.connection_pool.query_cache_enabled - refute ActiveRecord::Base.connection.query_cache_enabled - }.join - }.call({}) + Thread.new { + refute ActiveRecord::Base.connection_pool.query_cache_enabled + refute ActiveRecord::Base.connection.query_cache_enabled + }.join + }.call({}) + + end end private diff --git a/activerecord/test/cases/quoting_test.rb b/activerecord/test/cases/quoting_test.rb index 05b71638c1..5ff5e3c735 100644 --- a/activerecord/test/cases/quoting_test.rb +++ b/activerecord/test/cases/quoting_test.rb @@ -82,46 +82,46 @@ module ActiveRecord end def test_quote_with_quoted_id - assert_equal 1, @quoter.quote(Struct.new(:quoted_id).new(1), nil) + assert_equal 1, @quoter.quote(Struct.new(:quoted_id).new(1)) end def test_quote_nil - assert_equal "NULL", @quoter.quote(nil, nil) + assert_equal "NULL", @quoter.quote(nil) end def test_quote_true - assert_equal @quoter.quoted_true, @quoter.quote(true, nil) + assert_equal @quoter.quoted_true, @quoter.quote(true) end def test_quote_false - assert_equal @quoter.quoted_false, @quoter.quote(false, nil) + assert_equal @quoter.quoted_false, @quoter.quote(false) end def test_quote_float float = 1.2 - assert_equal float.to_s, @quoter.quote(float, nil) + assert_equal float.to_s, @quoter.quote(float) end def test_quote_integer integer = 1 - assert_equal integer.to_s, @quoter.quote(integer, nil) + assert_equal integer.to_s, @quoter.quote(integer) end def test_quote_bignum bignum = 1 << 100 - assert_equal bignum.to_s, @quoter.quote(bignum, nil) + assert_equal bignum.to_s, @quoter.quote(bignum) end def test_quote_bigdecimal bigdec = BigDecimal.new((1 << 100).to_s) - assert_equal bigdec.to_s("F"), @quoter.quote(bigdec, nil) + assert_equal bigdec.to_s("F"), @quoter.quote(bigdec) end def test_dates_and_times @quoter.extend(Module.new { def quoted_date(value) "lol" end }) - assert_equal "'lol'", @quoter.quote(Date.today, nil) - assert_equal "'lol'", @quoter.quote(Time.now, nil) - assert_equal "'lol'", @quoter.quote(DateTime.now, nil) + assert_equal "'lol'", @quoter.quote(Date.today) + assert_equal "'lol'", @quoter.quote(Time.now) + assert_equal "'lol'", @quoter.quote(DateTime.now) end def test_quoting_classes @@ -131,7 +131,7 @@ module ActiveRecord def test_crazy_object crazy = Object.new e = assert_raises(TypeError) do - @quoter.quote(crazy, nil) + @quoter.quote(crazy) end assert_equal "can't quote Object", e.message end diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb index 5dac3d064b..2444eccab1 100644 --- a/activerecord/test/cases/reflection_test.rb +++ b/activerecord/test/cases/reflection_test.rb @@ -86,8 +86,8 @@ class ReflectionTest < ActiveRecord::TestCase column = @first.column_for_attribute("attribute_that_doesnt_exist") assert_instance_of ActiveRecord::ConnectionAdapters::NullColumn, column assert_equal "attribute_that_doesnt_exist", column.name - assert_equal nil, column.sql_type - assert_equal nil, column.type + assert_nil column.sql_type + assert_nil column.type end def test_non_existent_types_are_identity_types @@ -100,7 +100,13 @@ class ReflectionTest < ActiveRecord::TestCase end def test_reflection_klass_for_nested_class_name - reflection = ActiveRecord::Reflection.create(:has_many, nil, nil, { class_name: "MyApplication::Business::Company" }, ActiveRecord::Base) + reflection = ActiveRecord::Reflection.create( + :has_many, + nil, + nil, + { class_name: "MyApplication::Business::Company" }, + Customer + ) assert_nothing_raised do assert_equal MyApplication::Business::Company, reflection.klass end @@ -398,6 +404,12 @@ class ReflectionTest < ActiveRecord::TestCase assert_equal Client, Firm.reflect_on_association(:unsorted_clients_with_symbol).klass end + def test_class_for_class_name + assert_deprecated do + assert_predicate ActiveRecord::Reflection.create(:has_many, :clients, nil, { class_name: Client }, Firm), :validate? + end + end + def test_join_table category = Struct.new(:table_name, :pluralize_table_names).new("categories", true) product = Struct.new(:table_name, :pluralize_table_names).new("products", true) diff --git a/activerecord/test/cases/relation/mutation_test.rb b/activerecord/test/cases/relation/mutation_test.rb index 966ae83a3f..de15eb9187 100644 --- a/activerecord/test/cases/relation/mutation_test.rb +++ b/activerecord/test/cases/relation/mutation_test.rb @@ -90,7 +90,7 @@ module ActiveRecord assert_equal [], relation.extending_values end - (Relation::SINGLE_VALUE_METHODS - [:lock, :reordering, :reverse_order, :create_with, :uniq]).each do |method| + (Relation::SINGLE_VALUE_METHODS - [:lock, :reordering, :reverse_order, :create_with]).each do |method| test "##{method}!" do assert relation.public_send("#{method}!", :foo).equal?(relation) assert_equal :foo, relation.public_send("#{method}_value") @@ -108,7 +108,7 @@ module ActiveRecord end test "#reorder!" do - @relation = self.relation.order("foo") + @relation = relation.order("foo") assert relation.reorder!("bar").equal?(relation) assert_equal ["bar"], relation.order_values @@ -161,22 +161,6 @@ module ActiveRecord test "distinct!" do relation.distinct! :foo assert_equal :foo, relation.distinct_value - - assert_deprecated do - assert_equal :foo, relation.uniq_value # deprecated access - end - end - - test "uniq! was replaced by distinct!" do - assert_deprecated(/use distinct! instead/) do - relation.uniq! :foo - end - - assert_deprecated(/use distinct_value instead/) do - assert_equal :foo, relation.uniq_value # deprecated access - end - - assert_equal :foo, relation.distinct_value end end end diff --git a/activerecord/test/cases/relation/or_test.rb b/activerecord/test/cases/relation/or_test.rb index 2796595523..abb7ca72dd 100644 --- a/activerecord/test/cases/relation/or_test.rb +++ b/activerecord/test/cases/relation/or_test.rb @@ -79,7 +79,7 @@ module ActiveRecord expected = Post.where("id = 1 or id = 2").to_a p = Post.where("id = 1") p.load - assert_equal p.loaded?, true + assert_equal true, p.loaded? assert_equal expected, p.or(Post.where("id = 2")).to_a end diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 96833ad428..dc6311e8bc 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -442,7 +442,7 @@ class RelationTest < ActiveRecord::TestCase assert_no_queries(ignore_none: false) do assert_equal 0, Developer.none.count assert_equal 0, Developer.none.calculate(:count, nil) - assert_equal nil, Developer.none.calculate(:average, "salary") + assert_nil Developer.none.calculate(:average, "salary") end end @@ -485,28 +485,28 @@ class RelationTest < ActiveRecord::TestCase def test_null_relation_average ac = Aircraft.new assert_equal Hash.new, ac.engines.group(:car_id).average(:id) - assert_equal nil, ac.engines.average(:id) + assert_nil ac.engines.average(:id) ac.save assert_equal Hash.new, ac.engines.group(:car_id).average(:id) - assert_equal nil, ac.engines.average(:id) + assert_nil ac.engines.average(:id) end def test_null_relation_minimum ac = Aircraft.new assert_equal Hash.new, ac.engines.group(:car_id).minimum(:id) - assert_equal nil, ac.engines.minimum(:id) + assert_nil ac.engines.minimum(:id) ac.save assert_equal Hash.new, ac.engines.group(:car_id).minimum(:id) - assert_equal nil, ac.engines.minimum(:id) + assert_nil ac.engines.minimum(:id) end def test_null_relation_maximum ac = Aircraft.new assert_equal Hash.new, ac.engines.group(:car_id).maximum(:id) - assert_equal nil, ac.engines.maximum(:id) + assert_nil ac.engines.maximum(:id) ac.save assert_equal Hash.new, ac.engines.group(:car_id).maximum(:id) - assert_equal nil, ac.engines.maximum(:id) + assert_nil ac.engines.maximum(:id) end def test_null_relation_in_where_condition @@ -840,15 +840,6 @@ class RelationTest < ActiveRecord::TestCase assert_equal author, authors.first end - class Mary < Author; end - - def test_find_by_classname - Author.create!(name: Mary.name) - assert_deprecated do - assert_equal 1, Author.where(name: Mary).size - end - end - def test_find_by_id_with_list_of_ar author = Author.first authors = Author.find_by_id([author]) @@ -1013,12 +1004,6 @@ class RelationTest < ActiveRecord::TestCase assert davids.loaded? end - def test_destroy_all_with_conditions_is_deprecated - assert_deprecated do - assert_difference("Author.count", -1) { Author.destroy_all(name: "David") } - end - end - def test_delete_all davids = Author.where(name: "David") @@ -1026,12 +1011,6 @@ class RelationTest < ActiveRecord::TestCase assert ! davids.loaded? end - def test_delete_all_with_conditions_is_deprecated - assert_deprecated do - assert_difference("Author.count", -1) { Author.delete_all(name: "David") } - end - end - def test_delete_all_loaded davids = Author.where(name: "David") @@ -1638,9 +1617,9 @@ class RelationTest < ActiveRecord::TestCase assert_equal "David", topic2.reload.author_name end - def test_update_on_relation_passing_active_record_object_is_deprecated + def test_update_on_relation_passing_active_record_object_is_not_permitted topic = Topic.create!(title: "Foo", author_name: nil) - assert_deprecated(/update/) do + assert_raises(ArgumentError) do Topic.where(id: topic.id).update(topic, title: "Bar") end end @@ -1654,17 +1633,11 @@ class RelationTest < ActiveRecord::TestCase assert_equal ["Foo", "Foo"], query.map(&:name) assert_sql(/DISTINCT/) do assert_equal ["Foo"], query.distinct.map(&:name) - assert_deprecated { assert_equal ["Foo"], query.uniq.map(&:name) } end assert_sql(/DISTINCT/) do assert_equal ["Foo"], query.distinct(true).map(&:name) - assert_deprecated { assert_equal ["Foo"], query.uniq(true).map(&:name) } end assert_equal ["Foo", "Foo"], query.distinct(true).distinct(false).map(&:name) - - assert_deprecated do - assert_equal ["Foo", "Foo"], query.uniq(true).uniq(false).map(&:name) - end end def test_doesnt_add_having_values_if_options_are_blank @@ -1814,7 +1787,7 @@ class RelationTest < ActiveRecord::TestCase end test "find_by returns nil if the record is missing" do - assert_equal nil, Post.all.find_by("1 = 0") + assert_nil Post.all.find_by("1 = 0") end test "find_by doesn't have implicit ordering" do diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb index 97096e31e7..bea78d2a95 100644 --- a/activerecord/test/cases/schema_dumper_test.rb +++ b/activerecord/test/cases/schema_dumper_test.rb @@ -148,12 +148,7 @@ class SchemaDumperTest < ActiveRecord::TestCase assert_match %r{c_int_4.*limit: 4}, output end - if current_adapter?(:SQLite3Adapter) - assert_match %r{c_int_5.*limit: 5}, output - assert_match %r{c_int_6.*limit: 6}, output - assert_match %r{c_int_7.*limit: 7}, output - assert_match %r{c_int_8.*limit: 8}, output - elsif current_adapter?(:OracleAdapter) + if current_adapter?(:SQLite3Adapter, :OracleAdapter) assert_match %r{c_int_5.*limit: 5}, output assert_match %r{c_int_6.*limit: 6}, output assert_match %r{c_int_7.*limit: 7}, output diff --git a/activerecord/test/cases/scoping/default_scoping_test.rb b/activerecord/test/cases/scoping/default_scoping_test.rb index b3dc979720..3a04f4bf7d 100644 --- a/activerecord/test/cases/scoping/default_scoping_test.rb +++ b/activerecord/test/cases/scoping/default_scoping_test.rb @@ -5,6 +5,7 @@ require "models/developer" require "models/computer" require "models/vehicle" require "models/cat" +require "concurrent/atomic/cyclic_barrier" class DefaultScopingTest < ActiveRecord::TestCase fixtures :developers, :posts, :comments @@ -50,7 +51,7 @@ class DefaultScopingTest < ActiveRecord::TestCase def test_default_scope_with_conditions_string assert_equal Developer.where(name: "David").map(&:id).sort, DeveloperCalledDavid.all.map(&:id).sort - assert_equal nil, DeveloperCalledDavid.create!.name + assert_nil DeveloperCalledDavid.create!.name end def test_default_scope_with_conditions_hash @@ -314,7 +315,7 @@ class DefaultScopingTest < ActiveRecord::TestCase end def test_create_attribute_overwrites_default_values - assert_equal nil, PoorDeveloperCalledJamis.create!(salary: nil).salary + assert_nil PoorDeveloperCalledJamis.create!(salary: nil).salary assert_equal 50000, PoorDeveloperCalledJamis.create!(name: "David").salary end @@ -437,12 +438,17 @@ class DefaultScopingTest < ActiveRecord::TestCase threads = [] assert_not_equal 1, ThreadsafeDeveloper.unscoped.count + barrier_1 = Concurrent::CyclicBarrier.new(2) + barrier_2 = Concurrent::CyclicBarrier.new(2) + threads << Thread.new do - Thread.current[:long_default_scope] = true + Thread.current[:default_scope_delay] = -> { barrier_1.wait; barrier_2.wait } assert_equal 1, ThreadsafeDeveloper.all.to_a.count ThreadsafeDeveloper.connection.close end threads << Thread.new do + Thread.current[:default_scope_delay] = -> { barrier_2.wait } + barrier_1.wait assert_equal 1, ThreadsafeDeveloper.all.to_a.count ThreadsafeDeveloper.connection.close end diff --git a/activerecord/test/cases/scoping/relation_scoping_test.rb b/activerecord/test/cases/scoping/relation_scoping_test.rb index 47310a151e..a1ae57fdbb 100644 --- a/activerecord/test/cases/scoping/relation_scoping_test.rb +++ b/activerecord/test/cases/scoping/relation_scoping_test.rb @@ -277,7 +277,7 @@ class NestedRelationScopingTest < ActiveRecord::TestCase assert_equal "David", Developer.first.name Developer.unscoped.where("name = 'Maiha'") do - assert_equal nil, Developer.first + assert_nil Developer.first end # ensure that scoping is restored diff --git a/activerecord/test/cases/secure_token_test.rb b/activerecord/test/cases/secure_token_test.rb index eda0229c26..7b9cbee40a 100644 --- a/activerecord/test/cases/secure_token_test.rb +++ b/activerecord/test/cases/secure_token_test.rb @@ -27,6 +27,6 @@ class SecureTokenTest < ActiveRecord::TestCase @user.token = "custom-secure-token" @user.save - assert_equal @user.token, "custom-secure-token" + assert_equal "custom-secure-token", @user.token end end diff --git a/activerecord/test/cases/serialized_attribute_test.rb b/activerecord/test/cases/serialized_attribute_test.rb index 1ccbf3ed4a..a469da0a5b 100644 --- a/activerecord/test/cases/serialized_attribute_test.rb +++ b/activerecord/test/cases/serialized_attribute_test.rb @@ -185,14 +185,14 @@ class SerializedAttributeTest < ActiveRecord::TestCase topic = Topic.new(content: true) assert topic.save topic = topic.reload - assert_equal topic.content, true + assert_equal true, topic.content end def test_serialized_boolean_value_false topic = Topic.new(content: false) assert topic.save topic = topic.reload - assert_equal topic.content, false + assert_equal false, topic.content end def test_serialize_with_coder @@ -211,7 +211,7 @@ class SerializedAttributeTest < ActiveRecord::TestCase topic.save! topic.reload assert_kind_of some_class, topic.content - assert_equal topic.content, some_class.new("my value") + assert_equal some_class.new("my value"), topic.content end def test_serialize_attribute_via_select_method_when_time_zone_available diff --git a/activerecord/test/cases/tasks/database_tasks_test.rb b/activerecord/test/cases/tasks/database_tasks_test.rb index d03231e711..5653fd83fd 100644 --- a/activerecord/test/cases/tasks/database_tasks_test.rb +++ b/activerecord/test/cases/tasks/database_tasks_test.rb @@ -61,7 +61,7 @@ module ActiveRecord instance = klazz.new klazz.stubs(:new).returns instance - instance.expects(:structure_dump).with("awesome-file.sql") + instance.expects(:structure_dump).with("awesome-file.sql", nil) ActiveRecord::Tasks::DatabaseTasks.register_task(/foo/, klazz) ActiveRecord::Tasks::DatabaseTasks.structure_dump({ "adapter" => :foo }, "awesome-file.sql") @@ -85,11 +85,23 @@ module ActiveRecord end end + class DatabaseTasksDumpSchemaCacheTest < ActiveRecord::TestCase + def test_dump_schema_cache + path = "/tmp/my_schema_cache.yml" + ActiveRecord::Tasks::DatabaseTasks.dump_schema_cache(ActiveRecord::Base.connection, path) + assert File.file?(path) + ensure + FileUtils.rm_rf(path) + end + end + class DatabaseTasksCreateAllTest < ActiveRecord::TestCase def setup @configurations = { "development" => { "database" => "my-db" } } ActiveRecord::Base.stubs(:configurations).returns(@configurations) + # To refrain from connecting to a newly created empty DB in sqlite3_mem tests + ActiveRecord::Base.connection_handler.stubs(:establish_connection) end def test_ignores_configurations_without_databases @@ -411,7 +423,7 @@ module ActiveRecord ADAPTERS_TASKS.each do |k, v| define_method("test_#{k}_structure_dump") do - eval("@#{v}").expects(:structure_dump).with("awesome-file.sql") + eval("@#{v}").expects(:structure_dump).with("awesome-file.sql", nil) ActiveRecord::Tasks::DatabaseTasks.structure_dump({ "adapter" => k }, "awesome-file.sql") end end @@ -422,7 +434,7 @@ module ActiveRecord ADAPTERS_TASKS.each do |k, v| define_method("test_#{k}_structure_load") do - eval("@#{v}").expects(:structure_load).with("awesome-file.sql") + eval("@#{v}").expects(:structure_load).with("awesome-file.sql", nil) ActiveRecord::Tasks::DatabaseTasks.structure_load({ "adapter" => k }, "awesome-file.sql") end end diff --git a/activerecord/test/cases/tasks/mysql_rake_test.rb b/activerecord/test/cases/tasks/mysql_rake_test.rb index dbe935808e..f30e0958c3 100644 --- a/activerecord/test/cases/tasks/mysql_rake_test.rb +++ b/activerecord/test/cases/tasks/mysql_rake_test.rb @@ -59,7 +59,7 @@ if current_adapter?(:Mysql2Adapter) def test_when_database_created_successfully_outputs_info_to_stdout ActiveRecord::Tasks::DatabaseTasks.create @configuration - assert_equal $stdout.string, "Created database 'my-app-db'\n" + assert_equal "Created database 'my-app-db'\n", $stdout.string end def test_create_when_database_exists_outputs_info_to_stderr @@ -69,7 +69,7 @@ if current_adapter?(:Mysql2Adapter) ActiveRecord::Tasks::DatabaseTasks.create @configuration - assert_equal $stderr.string, "Database 'my-app-db' already exists\n" + assert_equal "Database 'my-app-db' already exists\n", $stderr.string end end @@ -205,7 +205,7 @@ if current_adapter?(:Mysql2Adapter) def test_when_database_dropped_successfully_outputs_info_to_stdout ActiveRecord::Tasks::DatabaseTasks.drop @configuration - assert_equal $stdout.string, "Dropped database 'my-app-db'\n" + assert_equal "Dropped database 'my-app-db'\n", $stdout.string end end @@ -294,6 +294,17 @@ if current_adapter?(:Mysql2Adapter) ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, filename) end + def test_structure_dump_with_extra_flags + filename = "awesome-file.sql" + expected_command = ["mysqldump", "--result-file", filename, "--no-data", "--routines", "--skip-comments", "--noop", "test-db"] + + assert_called_with(Kernel, :system, expected_command, returns: true) do + with_structure_dump_flags(["--noop"]) do + ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, filename) + end + end + end + def test_warn_when_external_structure_dump_command_execution_fails filename = "awesome-file.sql" Kernel.expects(:system) @@ -323,6 +334,15 @@ if current_adapter?(:Mysql2Adapter) @configuration.merge("sslca" => "ca.crt"), filename) end + + private + def with_structure_dump_flags(flags) + old = ActiveRecord::Tasks::DatabaseTasks.structure_dump_flags + ActiveRecord::Tasks::DatabaseTasks.structure_dump_flags = flags + yield + ensure + ActiveRecord::Tasks::DatabaseTasks.structure_dump_flags = old + end end class MySQLStructureLoadTest < ActiveRecord::TestCase @@ -335,11 +355,23 @@ if current_adapter?(:Mysql2Adapter) def test_structure_load filename = "awesome-file.sql" - Kernel.expects(:system).with("mysql", "--execute", %{SET FOREIGN_KEY_CHECKS = 0; SOURCE #{filename}; SET FOREIGN_KEY_CHECKS = 1}, "--database", "test-db") - .returns(true) + expected_command = ["mysql", "--execute", %{SET FOREIGN_KEY_CHECKS = 0; SOURCE #{filename}; SET FOREIGN_KEY_CHECKS = 1}, "--database", "test-db", "--noop"] - ActiveRecord::Tasks::DatabaseTasks.structure_load(@configuration, filename) + assert_called_with(Kernel, :system, expected_command, returns: true) do + with_structure_load_flags(["--noop"]) do + ActiveRecord::Tasks::DatabaseTasks.structure_load(@configuration, filename) + end + end end + + private + def with_structure_load_flags(flags) + old = ActiveRecord::Tasks::DatabaseTasks.structure_load_flags + ActiveRecord::Tasks::DatabaseTasks.structure_load_flags = flags + yield + ensure + ActiveRecord::Tasks::DatabaseTasks.structure_load_flags = old + end end end end diff --git a/activerecord/test/cases/tasks/postgresql_rake_test.rb b/activerecord/test/cases/tasks/postgresql_rake_test.rb index b8c8ec88f0..a23100c32a 100644 --- a/activerecord/test/cases/tasks/postgresql_rake_test.rb +++ b/activerecord/test/cases/tasks/postgresql_rake_test.rb @@ -74,7 +74,7 @@ if current_adapter?(:PostgreSQLAdapter) def test_when_database_created_successfully_outputs_info_to_stdout ActiveRecord::Tasks::DatabaseTasks.create @configuration - assert_equal $stdout.string, "Created database 'my-app-db'\n" + assert_equal "Created database 'my-app-db'\n", $stdout.string end def test_create_when_database_exists_outputs_info_to_stderr @@ -84,7 +84,7 @@ if current_adapter?(:PostgreSQLAdapter) ActiveRecord::Tasks::DatabaseTasks.create @configuration - assert_equal $stderr.string, "Database 'my-app-db' already exists\n" + assert_equal "Database 'my-app-db' already exists\n", $stderr.string end end @@ -126,7 +126,7 @@ if current_adapter?(:PostgreSQLAdapter) def test_when_database_dropped_successfully_outputs_info_to_stdout ActiveRecord::Tasks::DatabaseTasks.drop @configuration - assert_equal $stdout.string, "Dropped database 'my-app-db'\n" + assert_equal "Dropped database 'my-app-db'\n", $stdout.string end end @@ -236,6 +236,16 @@ if current_adapter?(:PostgreSQLAdapter) ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, @filename) end + def test_structure_dump_with_extra_flags + expected_command = ["pg_dump", "-s", "-x", "-O", "-f", @filename, "--noop", "my-app-db"] + + assert_called_with(Kernel, :system, expected_command, returns: true) do + with_structure_dump_flags(["--noop"]) do + ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, @filename) + end + end + end + def test_structure_dump_with_schema_search_path @configuration["schema_search_path"] = "foo,bar" @@ -263,7 +273,6 @@ if current_adapter?(:PostgreSQLAdapter) end private - def with_dump_schemas(value, &block) old_dump_schemas = ActiveRecord::Base.dump_schemas ActiveRecord::Base.dump_schemas = value @@ -271,6 +280,14 @@ if current_adapter?(:PostgreSQLAdapter) ensure ActiveRecord::Base.dump_schemas = old_dump_schemas end + + def with_structure_dump_flags(flags) + old = ActiveRecord::Tasks::DatabaseTasks.structure_dump_flags + ActiveRecord::Tasks::DatabaseTasks.structure_dump_flags = flags + yield + ensure + ActiveRecord::Tasks::DatabaseTasks.structure_dump_flags = old + end end class PostgreSQLStructureLoadTest < ActiveRecord::TestCase @@ -293,12 +310,32 @@ if current_adapter?(:PostgreSQLAdapter) ActiveRecord::Tasks::DatabaseTasks.structure_load(@configuration, filename) end + def test_structure_load_with_extra_flags + filename = "awesome-file.sql" + expected_command = ["psql", "-v", "ON_ERROR_STOP=1", "-q", "-f", filename, "--noop", @configuration["database"]] + + assert_called_with(Kernel, :system, expected_command, returns: true) do + with_structure_load_flags(["--noop"]) do + ActiveRecord::Tasks::DatabaseTasks.structure_load(@configuration, filename) + end + end + end + def test_structure_load_accepts_path_with_spaces filename = "awesome file.sql" Kernel.expects(:system).with("psql", "-v", "ON_ERROR_STOP=1", "-q", "-f", filename, @configuration["database"]).returns(true) ActiveRecord::Tasks::DatabaseTasks.structure_load(@configuration, filename) end + + private + def with_structure_load_flags(flags) + old = ActiveRecord::Tasks::DatabaseTasks.structure_load_flags + ActiveRecord::Tasks::DatabaseTasks.structure_load_flags = flags + yield + ensure + ActiveRecord::Tasks::DatabaseTasks.structure_load_flags = old + end end end end diff --git a/activerecord/test/cases/tasks/sqlite_rake_test.rb b/activerecord/test/cases/tasks/sqlite_rake_test.rb index 141048bfe7..0d917f3f6c 100644 --- a/activerecord/test/cases/tasks/sqlite_rake_test.rb +++ b/activerecord/test/cases/tasks/sqlite_rake_test.rb @@ -34,7 +34,7 @@ if current_adapter?(:SQLite3Adapter) def test_when_db_created_successfully_outputs_info_to_stdout ActiveRecord::Tasks::DatabaseTasks.create @configuration, "/rails/root" - assert_equal $stdout.string, "Created database '#{@database}'\n" + assert_equal "Created database '#{@database}'\n", $stdout.string end def test_db_create_when_file_exists @@ -42,7 +42,7 @@ if current_adapter?(:SQLite3Adapter) ActiveRecord::Tasks::DatabaseTasks.create @configuration, "/rails/root" - assert_equal $stderr.string, "Database '#{@database}' already exists\n" + assert_equal "Database '#{@database}' already exists\n", $stderr.string end def test_db_create_with_file_does_nothing @@ -128,7 +128,7 @@ if current_adapter?(:SQLite3Adapter) def test_when_db_dropped_successfully_outputs_info_to_stdout ActiveRecord::Tasks::DatabaseTasks.drop @configuration, "/rails/root" - assert_equal $stdout.string, "Dropped database '#{@database}'\n" + assert_equal "Dropped database '#{@database}'\n", $stdout.string end end diff --git a/activerecord/test/cases/test_fixtures_test.rb b/activerecord/test/cases/test_fixtures_test.rb index 7090202a89..58d3bea3a2 100644 --- a/activerecord/test/cases/test_fixtures_test.rb +++ b/activerecord/test/cases/test_fixtures_test.rb @@ -6,25 +6,7 @@ class TestFixturesTest < ActiveRecord::TestCase @klass.include(ActiveRecord::TestFixtures) end - def test_deprecated_use_transactional_fixtures= - assert_deprecated "use use_transactional_tests= instead" do - @klass.use_transactional_fixtures = true - end - end - - def test_use_transactional_tests_prefers_use_transactional_fixtures - ActiveSupport::Deprecation.silence do - @klass.use_transactional_fixtures = false - end - - assert_equal false, @klass.use_transactional_tests - end - def test_use_transactional_tests_defaults_to_true - ActiveSupport::Deprecation.silence do - @klass.use_transactional_fixtures = nil - end - assert_equal true, @klass.use_transactional_tests end diff --git a/activerecord/test/cases/timestamp_test.rb b/activerecord/test/cases/timestamp_test.rb index cd83518e84..39b40e3411 100644 --- a/activerecord/test/cases/timestamp_test.rb +++ b/activerecord/test/cases/timestamp_test.rb @@ -422,7 +422,7 @@ class TimestampTest < ActiveRecord::TestCase self.table_name = "people" before_create do - self.born_at = self.created_at + self.born_at = created_at end end @@ -430,34 +430,19 @@ class TimestampTest < ActiveRecord::TestCase assert_not_equal person.born_at, nil end - def test_timestamp_attributes_for_create - toy = Toy.first - assert_equal [:created_at, :created_on], toy.send(:timestamp_attributes_for_create) - end - - def test_timestamp_attributes_for_update - toy = Toy.first - assert_equal [:updated_at, :updated_on], toy.send(:timestamp_attributes_for_update) - end - - def test_all_timestamp_attributes - toy = Toy.first - assert_equal [:created_at, :created_on, :updated_at, :updated_on], toy.send(:all_timestamp_attributes) - end - def test_timestamp_attributes_for_create_in_model toy = Toy.first - assert_equal [:created_at], toy.send(:timestamp_attributes_for_create_in_model) + assert_equal ["created_at"], toy.send(:timestamp_attributes_for_create_in_model) end def test_timestamp_attributes_for_update_in_model toy = Toy.first - assert_equal [:updated_at], toy.send(:timestamp_attributes_for_update_in_model) + assert_equal ["updated_at"], toy.send(:timestamp_attributes_for_update_in_model) end def test_all_timestamp_attributes_in_model toy = Toy.first - assert_equal [:created_at, :updated_at], toy.send(:all_timestamp_attributes_in_model) + assert_equal ["created_at", "updated_at"], toy.send(:all_timestamp_attributes_in_model) end def test_index_is_created_for_both_timestamps diff --git a/activerecord/test/cases/view_test.rb b/activerecord/test/cases/view_test.rb index 1f326d4b39..d055968a56 100644 --- a/activerecord/test/cases/view_test.rb +++ b/activerecord/test/cases/view_test.rb @@ -45,8 +45,7 @@ module ViewBehavior def test_table_exists view_name = Ebook.table_name - # TODO: switch this assertion around once we changed #tables to not return views. - ActiveSupport::Deprecation.silence { assert @connection.table_exists?(view_name), "'#{view_name}' table should exist" } + assert_not @connection.table_exists?(view_name), "'#{view_name}' table should not exist" end def test_views_ara_valid_data_sources @@ -131,8 +130,7 @@ if ActiveRecord::Base.connection.supports_views? def test_table_exists view_name = Paperback.table_name - # TODO: switch this assertion around once we changed #tables to not return views. - ActiveSupport::Deprecation.silence { assert @connection.table_exists?(view_name), "'#{view_name}' table should exist" } + assert_not @connection.table_exists?(view_name), "'#{view_name}' table should not exist" end def test_column_definitions @@ -202,8 +200,8 @@ if ActiveRecord::Base.connection.supports_views? end end end - end # end fo `if current_adapter?(:Mysql2Adapter, :PostgreSQLAdapter)` -end # end fo `if ActiveRecord::Base.connection.supports_views?` + end # end of `if current_adapter?(:Mysql2Adapter, :PostgreSQLAdapter)` +end # end of `if ActiveRecord::Base.connection.supports_views?` if ActiveRecord::Base.connection.respond_to?(:supports_materialized_views?) && ActiveRecord::Base.connection.supports_materialized_views? diff --git a/activerecord/test/cases/yaml_serialization_test.rb b/activerecord/test/cases/yaml_serialization_test.rb index 5192e5050a..1571b31329 100644 --- a/activerecord/test/cases/yaml_serialization_test.rb +++ b/activerecord/test/cases/yaml_serialization_test.rb @@ -95,7 +95,7 @@ class YamlSerializationTest < ActiveRecord::TestCase topic = YAML.load(yaml_fixture("rails_4_1")) assert topic.new_record? - assert_equal nil, topic.id + assert_nil topic.id assert_equal "The First Topic", topic.title assert_equal({ omg: :lol }, topic.content) end diff --git a/activerecord/test/fixtures/naked/yml/courses_with_invalid_key.yml b/activerecord/test/fixtures/naked/yml/courses_with_invalid_key.yml new file mode 100644 index 0000000000..6f9da79b45 --- /dev/null +++ b/activerecord/test/fixtures/naked/yml/courses_with_invalid_key.yml @@ -0,0 +1,3 @@ +one: + id: 1 +two: ['not a hash'] diff --git a/activerecord/test/fixtures/other_dogs.yml b/activerecord/test/fixtures/other_dogs.yml new file mode 100644 index 0000000000..b576861929 --- /dev/null +++ b/activerecord/test/fixtures/other_dogs.yml @@ -0,0 +1,2 @@ +lassie: + id: 1 diff --git a/activerecord/test/models/boolean.rb b/activerecord/test/models/boolean.rb index 7bae22e5f9..0da228aac2 100644 --- a/activerecord/test/models/boolean.rb +++ b/activerecord/test/models/boolean.rb @@ -1,2 +1,5 @@ class Boolean < ActiveRecord::Base + def has_fun + super + end end diff --git a/activerecord/test/models/company.rb b/activerecord/test/models/company.rb index 4561b3132b..20e37710e7 100644 --- a/activerecord/test/models/company.rb +++ b/activerecord/test/models/company.rb @@ -216,14 +216,12 @@ class Account < ActiveRecord::Base validate :check_empty_credit_limit - protected + private def check_empty_credit_limit errors.add("credit_limit", :blank) if credit_limit.blank? end - private - def private_method "Sir, yes sir!" end diff --git a/activerecord/test/models/company_in_module.rb b/activerecord/test/models/company_in_module.rb index 682f99e365..0782c1eff4 100644 --- a/activerecord/test/models/company_in_module.rb +++ b/activerecord/test/models/company_in_module.rb @@ -88,7 +88,7 @@ module MyApplication validate :check_empty_credit_limit - protected + private def check_empty_credit_limit errors.add("credit_card", :blank) if credit_card.blank? diff --git a/activerecord/test/models/customer.rb b/activerecord/test/models/customer.rb index 60af7c2247..3d40cb1ace 100644 --- a/activerecord/test/models/customer.rb +++ b/activerecord/test/models/customer.rb @@ -56,7 +56,7 @@ class GpsLocation end def ==(other) - self.latitude == other.latitude && self.longitude == other.longitude + latitude == other.latitude && longitude == other.longitude end end diff --git a/activerecord/test/models/developer.rb b/activerecord/test/models/developer.rb index 5ca1d37f6d..654830ba11 100644 --- a/activerecord/test/models/developer.rb +++ b/activerecord/test/models/developer.rb @@ -251,7 +251,7 @@ class ThreadsafeDeveloper < ActiveRecord::Base self.table_name = "developers" def self.default_scope - sleep 0.05 if Thread.current[:long_default_scope] + Thread.current[:default_scope_delay].call limit(1) end end @@ -260,3 +260,9 @@ class CachedDeveloper < ActiveRecord::Base self.table_name = "developers" self.cache_timestamp_format = :number end + +class DeveloperWithIncorrectlyOrderedHasManyThrough < ActiveRecord::Base + self.table_name = "developers" + has_many :companies, through: :contracts + has_many :contracts, foreign_key: :developer_id +end diff --git a/activerecord/test/models/other_dog.rb b/activerecord/test/models/other_dog.rb new file mode 100644 index 0000000000..418caf34be --- /dev/null +++ b/activerecord/test/models/other_dog.rb @@ -0,0 +1,5 @@ +require_dependency "models/arunit2_model" + +class OtherDog < ARUnit2Model + self.table_name = "dogs" +end diff --git a/activerecord/test/models/parrot.rb b/activerecord/test/models/parrot.rb index 5b693664d4..1e5f9285a8 100644 --- a/activerecord/test/models/parrot.rb +++ b/activerecord/test/models/parrot.rb @@ -13,6 +13,11 @@ class Parrot < ActiveRecord::Base def cancel_save_callback_method throw(:abort) end + + before_update :increment_updated_count + def increment_updated_count + self.updated_count += 1 + end end class LiveParrot < Parrot diff --git a/activerecord/test/models/subject.rb b/activerecord/test/models/subject.rb index 29e290825e..504f68a296 100644 --- a/activerecord/test/models/subject.rb +++ b/activerecord/test/models/subject.rb @@ -5,7 +5,7 @@ class Subject < ActiveRecord::Base # as otherwise synonym test was failing after_initialize :set_email_address - protected + private def set_email_address unless persisted? self.author_email_address = "test@test.com" diff --git a/activerecord/test/models/topic.rb b/activerecord/test/models/topic.rb index db04735d01..0420e2d15c 100644 --- a/activerecord/test/models/topic.rb +++ b/activerecord/test/models/topic.rb @@ -73,7 +73,7 @@ class Topic < ActiveRecord::Base write_attribute(:approved, val) end - protected + private def default_written_on self.written_on = Time.now unless attribute_present?("written_on") diff --git a/activerecord/test/models/user.rb b/activerecord/test/models/user.rb index 47649e0a77..c099c57e37 100644 --- a/activerecord/test/models/user.rb +++ b/activerecord/test/models/user.rb @@ -5,7 +5,7 @@ class User < ActiveRecord::Base has_secure_token :auth_token has_and_belongs_to_many :jobs_pool, - class_name: Job, + class_name: "Job", join_table: "jobs_pool" end diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index 658591b6ec..ba6f5de894 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -574,6 +574,7 @@ ActiveRecord::Schema.define do t.column :color, :string t.column :parrot_sti_class, :string t.column :killer_id, :integer + t.column :updated_count, :integer, default: 0 if subsecond_precision_supported? t.column :created_at, :datetime, precision: 0 t.column :created_on, :datetime, precision: 0 @@ -1050,3 +1051,5 @@ Professor.connection.create_table :courses_professors, id: false, force: true do t.references :course t.references :professor end + +OtherDog.connection.create_table :dogs, force: true diff --git a/activerecord/test/support/connection.rb b/activerecord/test/support/connection.rb index c9260398e2..bc5af36a28 100644 --- a/activerecord/test/support/connection.rb +++ b/activerecord/test/support/connection.rb @@ -2,6 +2,7 @@ require "active_support/logger" require "models/college" require "models/course" require "models/professor" +require "models/other_dog" module ARTest def self.connection_name diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 2a5c8deb11..873a39dbf6 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,3 +1,8 @@ +* Change return value of `Rational#duplicable?`, `ComplexClass#duplicable?` + to false. + + *utilum* + * Change return value of `NilClass#duplicable?`, `FalseClass#duplicable?`, `TrueClass#duplicable?`, `Symbol#duplicable?` and `Numeric#duplicable?` to true with Ruby 2.4+. These classes can dup with Ruby 2.4+. diff --git a/activesupport/MIT-LICENSE b/activesupport/MIT-LICENSE index 40235833ba..6b3cead1a7 100644 --- a/activesupport/MIT-LICENSE +++ b/activesupport/MIT-LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2005-2016 David Heinemeier Hansson +Copyright (c) 2005-2017 David Heinemeier Hansson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb index 2fc42e1975..267fa755c6 100644 --- a/activesupport/lib/active_support.rb +++ b/activesupport/lib/active_support.rb @@ -1,5 +1,5 @@ #-- -# Copyright (c) 2005-2016 David Heinemeier Hansson +# Copyright (c) 2005-2017 David Heinemeier Hansson # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb index 8f0a954882..d1bbd2f405 100644 --- a/activesupport/lib/active_support/cache.rb +++ b/activesupport/lib/active_support/cache.rb @@ -471,12 +471,12 @@ module ActiveSupport raise NotImplementedError.new("#{self.class.name} does not support clear") end - protected + private # Adds the namespace defined in the options to a pattern designed to # match keys. Implementations that support delete_matched should call # this method to translate a pattern that matches names into one that # matches namespaced keys. - def key_matcher(pattern, options) + def key_matcher(pattern, options) # :doc: prefix = options[:namespace].is_a?(Proc) ? options[:namespace].call : options[:namespace] if prefix source = pattern.source @@ -493,25 +493,24 @@ module ActiveSupport # Reads an entry from the cache implementation. Subclasses must implement # this method. - def read_entry(key, options) # :nodoc: + def read_entry(key, options) raise NotImplementedError.new end # Writes an entry to the cache implementation. Subclasses must implement # this method. - def write_entry(key, entry, options) # :nodoc: + def write_entry(key, entry, options) raise NotImplementedError.new end # Deletes an entry from the cache implementation. Subclasses must # implement this method. - def delete_entry(key, options) # :nodoc: + def delete_entry(key, options) raise NotImplementedError.new end - private # Merges the default options with ones specific to a method call. - def merged_options(call_options) # :nodoc: + def merged_options(call_options) if call_options options.merge(call_options) else @@ -522,7 +521,7 @@ module ActiveSupport # Expands key to be a consistent string value. Invokes +cache_key+ if # object responds to +cache_key+. Otherwise, +to_param+ method will be # called. If the key is a Hash, then keys will be sorted alphabetically. - def expanded_key(key) # :nodoc: + def expanded_key(key) return key.cache_key.to_s if key.respond_to?(:cache_key) case key diff --git a/activesupport/lib/active_support/cache/file_store.rb b/activesupport/lib/active_support/cache/file_store.rb index 34456a594f..d5c8585816 100644 --- a/activesupport/lib/active_support/cache/file_store.rb +++ b/activesupport/lib/active_support/cache/file_store.rb @@ -66,7 +66,7 @@ module ActiveSupport end end - protected + private def read_entry(key, options) if File.exist?(key) @@ -98,9 +98,8 @@ module ActiveSupport end end - private # Lock a file for a block so only one process can modify it at a time. - def lock_file(file_name, &block) # :nodoc: + def lock_file(file_name, &block) if File.exist?(file_name) File.open(file_name, "r+") do |f| begin diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb index e99d65bdf2..e09cee3335 100644 --- a/activesupport/lib/active_support/cache/mem_cache_store.rb +++ b/activesupport/lib/active_support/cache/mem_cache_store.rb @@ -26,7 +26,7 @@ module ActiveSupport class MemCacheStore < Store # Provide support for raw values in the local cache strategy. module LocalCacheWithRaw # :nodoc: - protected + private def read_entry(key, options) entry = super if options[:raw] && local_cache && entry @@ -35,7 +35,7 @@ module ActiveSupport entry end - def write_entry(key, entry, options) # :nodoc: + def write_entry(key, entry, options) if options[:raw] && local_cache raw_entry = Entry.new(entry.value.to_s) raw_entry.expires_at = entry.expires_at @@ -143,14 +143,14 @@ module ActiveSupport @data.stats end - protected + private # Read an entry from the cache. - def read_entry(key, options) # :nodoc: + def read_entry(key, options) rescue_error_with(nil) { deserialize_entry(@data.get(key, options)) } end # Write an entry to the cache. - def write_entry(key, entry, options) # :nodoc: + def write_entry(key, entry, options) method = options && options[:unless_exist] ? :add : :set value = options[:raw] ? entry.value.to_s : entry expires_in = options[:expires_in].to_i @@ -164,12 +164,10 @@ module ActiveSupport end # Delete an entry from the cache. - def delete_entry(key, options) # :nodoc: + def delete_entry(key, options) rescue_error_with(false) { @data.delete(key) } end - private - # Memcache keys are binaries. So we need to force their encoding to binary # before applying the regular expression to ensure we are escaping all # characters properly. diff --git a/activesupport/lib/active_support/cache/memory_store.rb b/activesupport/lib/active_support/cache/memory_store.rb index 968e72d765..fea072d91c 100644 --- a/activesupport/lib/active_support/cache/memory_store.rb +++ b/activesupport/lib/active_support/cache/memory_store.rb @@ -104,15 +104,15 @@ module ActiveSupport @monitor.synchronize(&block) end - protected + private PER_ENTRY_OVERHEAD = 240 - def cached_size(key, entry) # :nodoc: + def cached_size(key, entry) key.to_s.bytesize + entry.size + PER_ENTRY_OVERHEAD end - def read_entry(key, options) # :nodoc: + def read_entry(key, options) entry = @data[key] synchronize do if entry @@ -124,7 +124,7 @@ module ActiveSupport entry end - def write_entry(key, entry, options) # :nodoc: + def write_entry(key, entry, options) entry.dup_value! synchronize do old_entry = @data[key] @@ -141,7 +141,7 @@ module ActiveSupport end end - def delete_entry(key, options) # :nodoc: + def delete_entry(key, options) synchronize do @key_access.delete(key) entry = @data.delete(key) @@ -150,8 +150,6 @@ module ActiveSupport end end - private - def modify_value(name, amount, options) synchronize do options = merged_options(options) diff --git a/activesupport/lib/active_support/cache/null_store.rb b/activesupport/lib/active_support/cache/null_store.rb index 0564ce5312..550659fc56 100644 --- a/activesupport/lib/active_support/cache/null_store.rb +++ b/activesupport/lib/active_support/cache/null_store.rb @@ -25,15 +25,15 @@ module ActiveSupport def delete_matched(matcher, options = nil) end - protected - def read_entry(key, options) # :nodoc: + private + def read_entry(key, options) end - def write_entry(key, entry, options) # :nodoc: + def write_entry(key, entry, options) true end - def delete_entry(key, options) # :nodoc: + def delete_entry(key, options) false end end diff --git a/activesupport/lib/active_support/cache/strategy/local_cache.rb b/activesupport/lib/active_support/cache/strategy/local_cache.rb index 7652cae201..672eb2bb80 100644 --- a/activesupport/lib/active_support/cache/strategy/local_cache.rb +++ b/activesupport/lib/active_support/cache/strategy/local_cache.rb @@ -105,8 +105,8 @@ module ActiveSupport value end - protected - def read_entry(key, options) # :nodoc: + private + def read_entry(key, options) if cache = local_cache cache.fetch_entry(key) { super } else @@ -114,17 +114,17 @@ module ActiveSupport end end - def write_entry(key, entry, options) # :nodoc: + def write_entry(key, entry, options) local_cache.write_entry(key, entry, options) if local_cache super end - def delete_entry(key, options) # :nodoc: + def delete_entry(key, options) local_cache.delete_entry(key, options) if local_cache super end - def write_cache_value(name, value, options) # :nodoc: + def write_cache_value(name, value, options) name = normalize_key(name, options) cache = local_cache cache.mute do @@ -136,8 +136,6 @@ module ActiveSupport end end - private - def local_cache_key @local_cache_key ||= "#{self.class.name.underscore}_local_cache_#{object_id}".gsub(/[\/-]/, "_").to_sym end diff --git a/activesupport/lib/active_support/core_ext/array/grouping.rb b/activesupport/lib/active_support/core_ext/array/grouping.rb index ea9d85f6e3..0d798e5c4e 100644 --- a/activesupport/lib/active_support/core_ext/array/grouping.rb +++ b/activesupport/lib/active_support/core_ext/array/grouping.rb @@ -89,7 +89,7 @@ class Array # [1, 2, 3, 4, 5].split(3) # => [[1, 2], [4, 5]] # (1..10).to_a.split { |i| i % 3 == 0 } # => [[1, 2], [4, 5], [7, 8], [10]] def split(value = nil) - arr = self.dup + arr = dup result = [] if block_given? while (idx = arr.index { |i| yield i }) diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb index 2e64097933..d6f60cac04 100644 --- a/activesupport/lib/active_support/core_ext/date/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date/calculations.rb @@ -133,7 +133,7 @@ class Date # Allow Date to be compared with Time by converting to DateTime and relying on the <=> from there. def compare_with_coercion(other) if other.is_a?(Time) - self.to_datetime <=> other + to_datetime <=> other else compare_without_coercion(other) end diff --git a/activesupport/lib/active_support/core_ext/enumerable.rb b/activesupport/lib/active_support/core_ext/enumerable.rb index 8ebe758078..ae08fd90c1 100644 --- a/activesupport/lib/active_support/core_ext/enumerable.rb +++ b/activesupport/lib/active_support/core_ext/enumerable.rb @@ -115,9 +115,14 @@ end # and fall back to the compatible implementation, but that's much slower than # just calling the compat method in the first place. if Array.instance_methods(false).include?(:sum) && !(%w[a].sum rescue false) - class Array - alias :orig_sum :sum + # Using Refinements here in order not to expose our internal method + using Module.new { + refine Array do + alias :orig_sum :sum + end + } + class Array def sum(init = nil, &block) #:nodoc: if init.is_a?(Numeric) || first.is_a?(Numeric) init ||= 0 diff --git a/activesupport/lib/active_support/core_ext/load_error.rb b/activesupport/lib/active_support/core_ext/load_error.rb index 3cf7ea0a97..029d6dd449 100644 --- a/activesupport/lib/active_support/core_ext/load_error.rb +++ b/activesupport/lib/active_support/core_ext/load_error.rb @@ -1,6 +1,3 @@ -require "active_support/deprecation" -require "active_support/deprecation/proxy_wrappers" - class LoadError REGEXPS = [ /^no such file to load -- (.+)$/i, diff --git a/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb b/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb index 90989f4f3d..2c24081eb9 100644 --- a/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb +++ b/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb @@ -7,7 +7,8 @@ require "active_support/core_ext/regexp" class Module # Defines a class attribute and creates a class and instance reader methods. # The underlying class variable is set to +nil+, if it is not previously - # defined. + # defined. All class and instance methods created will be public, even if + # this method is called with a private or protected access modifier. # # module HairColors # mattr_reader :hair_colors @@ -76,7 +77,9 @@ class Module alias :cattr_reader :mattr_reader # Defines a class attribute and creates a class and instance writer methods to - # allow assignment to the attribute. + # allow assignment to the attribute. All class and instance methods created + # will be public, even if this method is called with a private or protected + # access modifier. # # module HairColors # mattr_writer :hair_colors @@ -142,6 +145,8 @@ class Module alias :cattr_writer :mattr_writer # Defines both class and instance accessors for class attributes. + # All class and instance methods created will be public, even if + # this method is called with a private or protected access modifier. # # module HairColors # mattr_accessor :hair_colors diff --git a/activesupport/lib/active_support/core_ext/object/duplicable.rb b/activesupport/lib/active_support/core_ext/object/duplicable.rb index ebe31b38ca..ea81df2bd8 100644 --- a/activesupport/lib/active_support/core_ext/object/duplicable.rb +++ b/activesupport/lib/active_support/core_ext/object/duplicable.rb @@ -73,7 +73,8 @@ end class Symbol begin - :symbol.dup + :symbol.dup # Ruby 2.4.x. + "symbol_from_string".to_sym.dup # Some symbols can't `dup` in Ruby 2.4.0. rescue TypeError # Symbols are not duplicable: @@ -121,3 +122,23 @@ class Method false end end + +class Complex + # Complexes are not duplicable: + # + # Complex(1).duplicable? # => false + # Complex(1).dup # => TypeError: can't copy Complex + def duplicable? + false + end +end + +class Rational + # Rationals are not duplicable: + # + # Rational(1).duplicable? # => false + # Rational(1).dup # => TypeError: can't copy Rational + def duplicable? + false + end +end diff --git a/activesupport/lib/active_support/core_ext/string/inflections.rb b/activesupport/lib/active_support/core_ext/string/inflections.rb index 4eabce79e2..7a2fc5c1b5 100644 --- a/activesupport/lib/active_support/core_ext/string/inflections.rb +++ b/activesupport/lib/active_support/core_ext/string/inflections.rb @@ -128,10 +128,10 @@ class String # Removes the module part from the constant expression in the string. # - # 'ActiveRecord::CoreExtensions::String::Inflections'.demodulize # => "Inflections" - # 'Inflections'.demodulize # => "Inflections" - # '::Inflections'.demodulize # => "Inflections" - # ''.demodulize # => '' + # 'ActiveSupport::Inflector::Inflections'.demodulize # => "Inflections" + # 'Inflections'.demodulize # => "Inflections" + # '::Inflections'.demodulize # => "Inflections" + # ''.demodulize # => '' # # See also +deconstantize+. def demodulize diff --git a/activesupport/lib/active_support/duration.rb b/activesupport/lib/active_support/duration.rb index ad2e139ea6..c9e8c8fdc4 100644 --- a/activesupport/lib/active_support/duration.rb +++ b/activesupport/lib/active_support/duration.rb @@ -159,9 +159,9 @@ module ActiveSupport delegate :<=>, to: :value - protected + private - def sum(sign, time = ::Time.current) #:nodoc: + def sum(sign, time = ::Time.current) parts.inject(time) do |t, (type, number)| if t.acts_like?(:time) || t.acts_like?(:date) if type == :seconds @@ -179,9 +179,7 @@ module ActiveSupport end end - private - - def method_missing(method, *args, &block) #:nodoc: + def method_missing(method, *args, &block) value.send(method, *args, &block) end end diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb index 5919b89338..79e7feaf47 100644 --- a/activesupport/lib/active_support/hash_with_indifferent_access.rb +++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb @@ -269,6 +269,10 @@ module ActiveSupport dup.tap { |hash| hash.transform_values!(*args, &block) } end + def compact + dup.compact! + end + # Convert to a regular hash with string keys. def to_hash _new_hash = Hash.new @@ -280,12 +284,12 @@ module ActiveSupport _new_hash end - protected - def convert_key(key) + private + def convert_key(key) # :doc: key.kind_of?(Symbol) ? key.to_s : key end - def convert_value(value, options = {}) + def convert_value(value, options = {}) # :doc: if value.is_a? Hash if options[:for] == :to_hash value.to_hash @@ -302,7 +306,7 @@ module ActiveSupport end end - def set_defaults(target) + def set_defaults(target) # :doc: if default_proc target.default_proc = default_proc.dup else diff --git a/activesupport/lib/active_support/i18n_railtie.rb b/activesupport/lib/active_support/i18n_railtie.rb index 08c9ef8f02..b94368df14 100644 --- a/activesupport/lib/active_support/i18n_railtie.rb +++ b/activesupport/lib/active_support/i18n_railtie.rb @@ -21,8 +21,6 @@ module I18n I18n::Railtie.initialize_i18n(app) end - protected - @i18n_inited = false # Setup i18n configuration. diff --git a/activesupport/lib/active_support/inflector/inflections.rb b/activesupport/lib/active_support/inflector/inflections.rb index aa68f9ec9e..c47a2e34e1 100644 --- a/activesupport/lib/active_support/inflector/inflections.rb +++ b/activesupport/lib/active_support/inflector/inflections.rb @@ -1,5 +1,6 @@ require "concurrent/map" require "active_support/core_ext/array/prepend_and_append" +require "active_support/core_ext/regexp" require "active_support/i18n" module ActiveSupport @@ -43,13 +44,14 @@ module ActiveSupport end def add(words) - concat(words.flatten.map(&:downcase)) - @regex_array += map { |word| to_regex(word) } + words = words.flatten.map(&:downcase) + concat(words) + @regex_array += words.map { |word| to_regex(word) } self end def uncountable?(str) - @regex_array.any? { |regex| regex === str } + @regex_array.any? { |regex| regex.match? str } end private diff --git a/activesupport/lib/active_support/inflector/methods.rb b/activesupport/lib/active_support/inflector/methods.rb index fa063af3f4..8ccb735c6d 100644 --- a/activesupport/lib/active_support/inflector/methods.rb +++ b/activesupport/lib/active_support/inflector/methods.rb @@ -198,10 +198,10 @@ module ActiveSupport # Removes the module part from the expression in the string. # - # demodulize('ActiveRecord::CoreExtensions::String::Inflections') # => "Inflections" - # demodulize('Inflections') # => "Inflections" - # demodulize('::Inflections') # => "Inflections" - # demodulize('') # => "" + # demodulize('ActiveSupport::Inflector::Inflections') # => "Inflections" + # demodulize('Inflections') # => "Inflections" + # demodulize('::Inflections') # => "Inflections" + # demodulize('') # => "" # # See also #deconstantize. def demodulize(path) @@ -361,7 +361,7 @@ module ActiveSupport # # const_regexp("Foo::Bar::Baz") # => "Foo(::Bar(::Baz)?)?" # const_regexp("::") # => "::" - def const_regexp(camel_cased_word) #:nodoc: + def const_regexp(camel_cased_word) parts = camel_cased_word.split("::".freeze) return Regexp.escape(camel_cased_word) if parts.blank? diff --git a/activesupport/lib/active_support/log_subscriber.rb b/activesupport/lib/active_support/log_subscriber.rb index cb6ea095b8..e2c4f33565 100644 --- a/activesupport/lib/active_support/log_subscriber.rb +++ b/activesupport/lib/active_support/log_subscriber.rb @@ -85,7 +85,7 @@ module ActiveSupport logger.error "Could not log #{name.inspect} event. #{e.class}: #{e.message} #{e.backtrace}" end - protected + private %w(info debug warn error fatal unknown).each do |level| class_eval <<-METHOD, __FILE__, __LINE__ + 1 @@ -99,7 +99,7 @@ module ActiveSupport # option is set to +true+, it also adds bold to the string. This is based # on the Highline implementation and will automatically append CLEAR to the # end of the returned String. - def color(text, color, bold = false) + def color(text, color, bold = false) # :doc: return text unless colorize_logging color = self.class.const_get(color.upcase) if color.is_a?(Symbol) bold = bold ? BOLD : "" diff --git a/activesupport/lib/active_support/message_encryptor.rb b/activesupport/lib/active_support/message_encryptor.rb index 7b33dc3481..0671469788 100644 --- a/activesupport/lib/active_support/message_encryptor.rb +++ b/activesupport/lib/active_support/message_encryptor.rb @@ -14,10 +14,10 @@ module ActiveSupport # where you don't want users to be able to determine the value of the payload. # # salt = SecureRandom.random_bytes(64) - # key = ActiveSupport::KeyGenerator.new('password').generate_key(salt) # => "\x89\xE0\x156\xAC..." - # crypt = ActiveSupport::MessageEncryptor.new(key) # => #<ActiveSupport::MessageEncryptor ...> - # encrypted_data = crypt.encrypt_and_sign('my secret data') # => "NlFBTTMwOUV5UlA1QlNEN2xkY2d6eThYWWh..." - # crypt.decrypt_and_verify(encrypted_data) # => "my secret data" + # key = ActiveSupport::KeyGenerator.new('password').generate_key(salt, 32) # => "\x89\xE0\x156\xAC..." + # crypt = ActiveSupport::MessageEncryptor.new(key) # => #<ActiveSupport::MessageEncryptor ...> + # encrypted_data = crypt.encrypt_and_sign('my secret data') # => "NlFBTTMwOUV5UlA1QlNEN2xkY2d6eThYWWh..." + # crypt.decrypt_and_verify(encrypted_data) # => "my secret data" class MessageEncryptor DEFAULT_CIPHER = "aes-256-cbc" diff --git a/activesupport/lib/active_support/multibyte/chars.rb b/activesupport/lib/active_support/multibyte/chars.rb index 262f25b874..65d6259a06 100644 --- a/activesupport/lib/active_support/multibyte/chars.rb +++ b/activesupport/lib/active_support/multibyte/chars.rb @@ -210,9 +210,9 @@ module ActiveSupport #:nodoc: end end - protected + private - def translate_offset(byte_offset) #:nodoc: + def translate_offset(byte_offset) return nil if byte_offset.nil? return 0 if @wrapped_string == "" @@ -224,7 +224,7 @@ module ActiveSupport #:nodoc: end end - def chars(string) #:nodoc: + def chars(string) self.class.new(string) end end diff --git a/activesupport/lib/active_support/multibyte/unicode.rb b/activesupport/lib/active_support/multibyte/unicode.rb index 7842264b39..05cfb249c3 100644 --- a/activesupport/lib/active_support/multibyte/unicode.rb +++ b/activesupport/lib/active_support/multibyte/unicode.rb @@ -358,7 +358,7 @@ module ActiveSupport private - def apply_mapping(string, mapping) #:nodoc: + def apply_mapping(string, mapping) database.codepoints string.each_codepoint.map do |codepoint| cp = database.codepoints[codepoint] diff --git a/activesupport/lib/active_support/number_helper/number_converter.rb b/activesupport/lib/active_support/number_helper/number_converter.rb index c485a6af63..ce363287cf 100644 --- a/activesupport/lib/active_support/number_helper/number_converter.rb +++ b/activesupport/lib/active_support/number_helper/number_converter.rb @@ -139,17 +139,17 @@ module ActiveSupport @options ||= format_options.merge(opts) end - def format_options #:nodoc: + def format_options default_format_options.merge!(i18n_format_options) end - def default_format_options #:nodoc: + def default_format_options options = DEFAULTS[:format].dup options.merge!(DEFAULTS[namespace][:format]) if namespace options end - def i18n_format_options #:nodoc: + def i18n_format_options locale = opts[:locale] options = I18n.translate(:'number.format', locale: locale, default: {}).dup @@ -160,7 +160,7 @@ module ActiveSupport options end - def translate_number_value_with_default(key, i18n_options = {}) #:nodoc: + def translate_number_value_with_default(key, i18n_options = {}) I18n.translate(key, { default: default_value(key), scope: :number }.merge!(i18n_options)) end @@ -172,7 +172,7 @@ module ActiveSupport key.split(".").reduce(DEFAULTS) { |defaults, k| defaults[k.to_sym] } end - def valid_float? #:nodoc: + def valid_float? Float(number) rescue ArgumentError, TypeError false diff --git a/activesupport/lib/active_support/per_thread_registry.rb b/activesupport/lib/active_support/per_thread_registry.rb index 9e6d8d4fd8..02431704d3 100644 --- a/activesupport/lib/active_support/per_thread_registry.rb +++ b/activesupport/lib/active_support/per_thread_registry.rb @@ -45,8 +45,8 @@ module ActiveSupport Thread.current[@per_thread_registry_key] ||= new end - protected - def method_missing(name, *args, &block) # :nodoc: + private + def method_missing(name, *args, &block) # Caches the method definition as a singleton method of the receiver. # # By letting #delegate handle it, we avoid an enclosure that'll capture args. diff --git a/activesupport/lib/active_support/rescuable.rb b/activesupport/lib/active_support/rescuable.rb index e0190fc3b0..ee6592fb5a 100644 --- a/activesupport/lib/active_support/rescuable.rb +++ b/activesupport/lib/active_support/rescuable.rb @@ -36,7 +36,7 @@ module ActiveSupport # render xml: exception, status: 500 # end # - # protected + # private # def deny_access # ... # end diff --git a/activesupport/lib/active_support/subscriber.rb b/activesupport/lib/active_support/subscriber.rb index 2bde575698..2924139755 100644 --- a/activesupport/lib/active_support/subscriber.rb +++ b/activesupport/lib/active_support/subscriber.rb @@ -52,11 +52,15 @@ module ActiveSupport @@subscribers ||= [] end + # TODO Change this to private once we've dropped Ruby 2.2 support. + # Workaround for Ruby 2.2 "private attribute?" warning. protected attr_reader :subscriber, :notifier, :namespace - def add_event_subscriber(event) + private + + def add_event_subscriber(event) # :doc: return if %w{ start finish }.include?(event.to_s) pattern = "#{event}.#{namespace}" diff --git a/activesupport/lib/active_support/testing/isolation.rb b/activesupport/lib/active_support/testing/isolation.rb index 14edd13a8f..54c3263efa 100644 --- a/activesupport/lib/active_support/testing/isolation.rb +++ b/activesupport/lib/active_support/testing/isolation.rb @@ -43,7 +43,7 @@ module ActiveSupport end } end - result = Marshal.dump(self.dup) + result = Marshal.dump(dup) end write.puts [result].pack("m") @@ -78,7 +78,7 @@ module ActiveSupport "ISOLATION_OUTPUT" => tmpfile.path } - test_opts = "-n#{self.class.name}##{self.name}" + test_opts = "-n#{self.class.name}##{name}" load_path_args = [] $-I.each do |p| diff --git a/activesupport/lib/active_support/testing/time_helpers.rb b/activesupport/lib/active_support/testing/time_helpers.rb index e2f008b4b7..07c9be0604 100644 --- a/activesupport/lib/active_support/testing/time_helpers.rb +++ b/activesupport/lib/active_support/testing/time_helpers.rb @@ -10,7 +10,7 @@ module ActiveSupport @stubs = Concurrent::Map.new { |h, k| h[k] = {} } end - def stub_object(object, method_name, return_value) + def stub_object(object, method_name, &block) if stub = stubbing(object, method_name) unstub_object(stub) end @@ -20,7 +20,7 @@ module ActiveSupport @stubs[object.object_id][method_name] = Stub.new(object, method_name, new_name) object.singleton_class.send :alias_method, new_name, method_name - object.define_singleton_method(method_name) { return_value } + object.define_singleton_method(method_name, &block) end def unstub_all! @@ -134,9 +134,9 @@ module ActiveSupport now = date_or_time.to_time.change(usec: 0) end - simple_stubs.stub_object(Time, :now, now) - simple_stubs.stub_object(Date, :today, now.to_date) - simple_stubs.stub_object(DateTime, :now, now.to_datetime) + simple_stubs.stub_object(Time, :now) { at(now.to_i) } + simple_stubs.stub_object(Date, :today) { jd(now.to_date.jd) } + simple_stubs.stub_object(DateTime, :now) { jd(now.to_date.jd, now.hour, now.min, now.sec, Rational(now.utc_offset, 86400)) } if block_given? begin diff --git a/activesupport/lib/active_support/xml_mini.rb b/activesupport/lib/active_support/xml_mini.rb index e3203ef076..782fb41288 100644 --- a/activesupport/lib/active_support/xml_mini.rb +++ b/activesupport/lib/active_support/xml_mini.rb @@ -73,7 +73,7 @@ module ActiveSupport begin BigDecimal(number) rescue ArgumentError - BigDecimal('0') + BigDecimal("0") end else BigDecimal(number) @@ -159,7 +159,7 @@ module ActiveSupport key end - protected + private def _dasherize(key) # $2 must be a non-greedy regex for this to work @@ -168,7 +168,7 @@ module ActiveSupport end # TODO: Add support for other encodings - def _parse_binary(bin, entity) #:nodoc: + def _parse_binary(bin, entity) case entity["encoding"] when "base64" ::Base64.decode64(bin) @@ -185,8 +185,6 @@ module ActiveSupport f end - private - def current_thread_backend Thread.current[:xml_mini_backend] end diff --git a/activesupport/lib/active_support/xml_mini/libxmlsax.rb b/activesupport/lib/active_support/xml_mini/libxmlsax.rb index 4edb0bd2b1..8a43b05b17 100644 --- a/activesupport/lib/active_support/xml_mini/libxmlsax.rb +++ b/activesupport/lib/active_support/xml_mini/libxmlsax.rb @@ -73,7 +73,7 @@ module ActiveSupport LibXML::XML::Error.set_handler(&LibXML::XML::Error::QUIET_HANDLER) parser = LibXML::XML::SaxParser.io(data) - document = self.document_class.new + document = document_class.new parser.callbacks = document parser.parse diff --git a/activesupport/lib/active_support/xml_mini/nokogirisax.rb b/activesupport/lib/active_support/xml_mini/nokogirisax.rb index b8b85146c5..7388bea5d8 100644 --- a/activesupport/lib/active_support/xml_mini/nokogirisax.rb +++ b/activesupport/lib/active_support/xml_mini/nokogirisax.rb @@ -76,7 +76,7 @@ module ActiveSupport {} else data.ungetc(char) - document = self.document_class.new + document = document_class.new parser = Nokogiri::XML::SAX::Parser.new(document) parser.parse(data) document.hash diff --git a/activesupport/test/abstract_unit.rb b/activesupport/test/abstract_unit.rb index 4e564591b4..6b9daf0fe2 100644 --- a/activesupport/test/abstract_unit.rb +++ b/activesupport/test/abstract_unit.rb @@ -3,8 +3,8 @@ ORIG_ARGV = ARGV.dup require "active_support/core_ext/kernel/reporting" silence_warnings do - Encoding.default_internal = "UTF-8" - Encoding.default_external = "UTF-8" + Encoding.default_internal = Encoding::UTF_8 + Encoding.default_external = Encoding::UTF_8 end require "active_support/testing/autorun" diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb index 73b5a7d2a8..c543122d91 100644 --- a/activesupport/test/caching_test.rb +++ b/activesupport/test/caching_test.rb @@ -316,7 +316,7 @@ module CacheStoreBehavior def test_should_read_and_write_nil assert @cache.write("foo", nil) - assert_equal nil, @cache.read("foo") + assert_nil @cache.read("foo") end def test_should_read_and_write_false @@ -464,7 +464,7 @@ module CacheStoreBehavior Time.stub(:now, Time.at(time)) do result = @cache.fetch("foo") do - assert_equal nil, @cache.read("foo") + assert_nil @cache.read("foo") "baz" end assert_equal "baz", result @@ -476,7 +476,7 @@ module CacheStoreBehavior @cache.write("foo", "bar", expires_in: 60) Time.stub(:now, time + 71) do result = @cache.fetch("foo", race_condition_ttl: 10) do - assert_equal nil, @cache.read("foo") + assert_nil @cache.read("foo") "baz" end assert_equal "baz", result @@ -675,9 +675,9 @@ module LocalCacheBehavior def test_local_cache_of_read_nil @cache.with_local_cache do - assert_equal nil, @cache.read("foo") + assert_nil @cache.read("foo") @cache.send(:bypass_local_cache) { @cache.write "foo", "bar" } - assert_equal nil, @cache.read("foo") + assert_nil @cache.read("foo") end end diff --git a/activesupport/test/callbacks_test.rb b/activesupport/test/callbacks_test.rb index 22f66978f8..28caa30bf1 100644 --- a/activesupport/test/callbacks_test.rb +++ b/activesupport/test/callbacks_test.rb @@ -887,7 +887,7 @@ module CallbacksTest def test_returning_false_does_not_halt_callback_if_config_variable_is_not_set obj = CallbackFalseTerminator.new obj.save - assert_equal nil, obj.halted + assert_nil obj.halted assert obj.saved end end @@ -900,7 +900,7 @@ module CallbacksTest def test_returning_false_does_not_halt_callback_if_config_variable_is_true obj = CallbackFalseTerminator.new obj.save - assert_equal nil, obj.halted + assert_nil obj.halted assert obj.saved end end @@ -913,7 +913,7 @@ module CallbacksTest def test_returning_false_does_not_halt_callback_if_config_variable_is_false obj = CallbackFalseTerminator.new obj.save - assert_equal nil, obj.halted + assert_nil obj.halted assert obj.saved end end @@ -1040,7 +1040,7 @@ module CallbacksTest set_callback :foo, :before, :foo, if: callback def run; run_callbacks :foo; end private - def foo; end + def foo; end } object = klass.new object.run diff --git a/activesupport/test/class_cache_test.rb b/activesupport/test/class_cache_test.rb index c618fea81a..004b4dc9ce 100644 --- a/activesupport/test/class_cache_test.rb +++ b/activesupport/test/class_cache_test.rb @@ -61,7 +61,7 @@ module ActiveSupport def test_safe_get_constantizes_doesnt_fail_on_invalid_names assert @cache.empty? - assert_equal nil, @cache.safe_get("OmgTotallyInvalidConstantName") + assert_nil @cache.safe_get("OmgTotallyInvalidConstantName") end def test_new_rejects_strings diff --git a/activesupport/test/core_ext/date_time_ext_test.rb b/activesupport/test/core_ext/date_time_ext_test.rb index 81777889ec..e9be181749 100644 --- a/activesupport/test/core_ext/date_time_ext_test.rb +++ b/activesupport/test/core_ext/date_time_ext_test.rb @@ -376,7 +376,7 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase assert_equal 1, DateTime.civil(2000) <=> Time.utc(1999, 12, 31, 23, 59, 59).to_s assert_equal 0, DateTime.civil(2000) <=> Time.utc(2000, 1, 1, 0, 0, 0).to_s assert_equal(-1, DateTime.civil(2000) <=> Time.utc(2000, 1, 1, 0, 0, 1).to_s) - assert_equal nil, DateTime.civil(2000) <=> "Invalid as Time" + assert_nil DateTime.civil(2000) <=> "Invalid as Time" end def test_compare_with_integer diff --git a/activesupport/test/core_ext/duration_test.rb b/activesupport/test/core_ext/duration_test.rb index 26a9ea4aee..fc0dd41d0e 100644 --- a/activesupport/test/core_ext/duration_test.rb +++ b/activesupport/test/core_ext/duration_test.rb @@ -21,7 +21,7 @@ class DurationTest < ActiveSupport::TestCase end def test_instance_of - assert 1.minute.instance_of?(Fixnum) + assert 1.minute.instance_of?(1.class) assert 2.days.instance_of?(ActiveSupport::Duration) assert !3.second.instance_of?(Numeric) end @@ -196,7 +196,7 @@ class DurationTest < ActiveSupport::TestCase assert_nothing_raised do 1.minute.times { counter += 1 } end - assert_equal counter, 60 + assert_equal 60, counter end def test_as_json @@ -213,7 +213,7 @@ class DurationTest < ActiveSupport::TestCase when 1.day "ok" end - assert_equal cased, "ok" + assert_equal "ok", cased end def test_respond_to diff --git a/activesupport/test/core_ext/enumerable_test.rb b/activesupport/test/core_ext/enumerable_test.rb index 5b839f1032..4f1ab993b8 100644 --- a/activesupport/test/core_ext/enumerable_test.rb +++ b/activesupport/test/core_ext/enumerable_test.rb @@ -172,7 +172,7 @@ class EnumerableTests < ActiveSupport::TestCase payments.index_by(&:price)) assert_equal Enumerator, payments.index_by.class if Enumerator.method_defined? :size - assert_equal nil, payments.index_by.size + assert_nil payments.index_by.size assert_equal 42, (1..42).index_by.size end assert_equal({ 5 => Payment.new(5), 15 => Payment.new(15), 10 => Payment.new(10) }, diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb index 8b816b3aad..05813ad388 100644 --- a/activesupport/test/core_ext/hash_ext_test.rb +++ b/activesupport/test/core_ext/hash_ext_test.rb @@ -111,7 +111,7 @@ class HashExtTest < ActiveSupport::TestCase transformed_hash = @mixed.dup transformed_hash.transform_keys! { |key| key.to_s.upcase } assert_equal @upcase_strings, transformed_hash - assert_equal @mixed, :a => 1, "b" => 2 + assert_equal({ :a => 1, "b" => 2 }, @mixed) end def test_deep_transform_keys! @@ -127,7 +127,7 @@ class HashExtTest < ActiveSupport::TestCase transformed_hash = @nested_mixed.deep_dup transformed_hash.deep_transform_keys! { |key| key.to_s.upcase } assert_equal @nested_upcase_strings, transformed_hash - assert_equal @nested_mixed, "a" => { b: { "c" => 3 } } + assert_equal({ "a" => { b: { "c" => 3 } } }, @nested_mixed) end def test_symbolize_keys @@ -167,7 +167,7 @@ class HashExtTest < ActiveSupport::TestCase transformed_hash = @mixed.dup transformed_hash.deep_symbolize_keys! assert_equal @symbols, transformed_hash - assert_equal @mixed, :a => 1, "b" => 2 + assert_equal({ :a => 1, "b" => 2 }, @mixed) end def test_deep_symbolize_keys! @@ -183,7 +183,7 @@ class HashExtTest < ActiveSupport::TestCase transformed_hash = @nested_mixed.deep_dup transformed_hash.deep_symbolize_keys! assert_equal @nested_symbols, transformed_hash - assert_equal @nested_mixed, "a" => { b: { "c" => 3 } } + assert_equal({ "a" => { b: { "c" => 3 } } }, @nested_mixed) end def test_symbolize_keys_preserves_keys_that_cant_be_symbolized @@ -243,7 +243,7 @@ class HashExtTest < ActiveSupport::TestCase transformed_hash = @mixed.dup transformed_hash.stringify_keys! assert_equal @strings, transformed_hash - assert_equal @mixed, :a => 1, "b" => 2 + assert_equal({ :a => 1, "b" => 2 }, @mixed) end def test_deep_stringify_keys! @@ -259,7 +259,7 @@ class HashExtTest < ActiveSupport::TestCase transformed_hash = @nested_mixed.deep_dup transformed_hash.deep_stringify_keys! assert_equal @nested_strings, transformed_hash - assert_equal @nested_mixed, "a" => { b: { "c" => 3 } } + assert_equal({ "a" => { b: { "c" => 3 } } }, @nested_mixed) end def test_symbolize_keys_for_hash_with_indifferent_access @@ -390,8 +390,8 @@ class HashExtTest < ActiveSupport::TestCase assert_equal 1, hash[:a] assert_equal true, hash[:b] assert_equal false, hash[:c] - assert_equal nil, hash[:d] - assert_equal nil, hash[:e] + assert_nil hash[:d] + assert_nil hash[:e] end def test_indifferent_reading_with_nonnil_default @@ -404,7 +404,7 @@ class HashExtTest < ActiveSupport::TestCase assert_equal 1, hash[:a] assert_equal true, hash[:b] assert_equal false, hash[:c] - assert_equal nil, hash[:d] + assert_nil hash[:d] assert_equal 1, hash[:e] end @@ -414,11 +414,11 @@ class HashExtTest < ActiveSupport::TestCase hash["b"] = 2 hash[3] = 3 - assert_equal hash["a"], 1 - assert_equal hash["b"], 2 - assert_equal hash[:a], 1 - assert_equal hash[:b], 2 - assert_equal hash[3], 3 + assert_equal 1, hash["a"] + assert_equal 2, hash["b"] + assert_equal 1, hash[:a] + assert_equal 2, hash[:b] + assert_equal 3, hash[3] end def test_indifferent_update @@ -430,16 +430,16 @@ class HashExtTest < ActiveSupport::TestCase updated_with_symbols = hash.update(@symbols) updated_with_mixed = hash.update(@mixed) - assert_equal updated_with_strings[:a], 1 - assert_equal updated_with_strings["a"], 1 - assert_equal updated_with_strings["b"], 2 + assert_equal 1, updated_with_strings[:a] + assert_equal 1, updated_with_strings["a"] + assert_equal 2, updated_with_strings["b"] - assert_equal updated_with_symbols[:a], 1 - assert_equal updated_with_symbols["b"], 2 - assert_equal updated_with_symbols[:b], 2 + assert_equal 1, updated_with_symbols[:a] + assert_equal 2, updated_with_symbols["b"] + assert_equal 2, updated_with_symbols[:b] - assert_equal updated_with_mixed[:a], 1 - assert_equal updated_with_mixed["b"], 2 + assert_equal 1, updated_with_mixed[:a] + assert_equal 2, updated_with_mixed["b"] assert [updated_with_strings, updated_with_symbols, updated_with_mixed].all? { |h| h.keys.size == 2 } end @@ -447,7 +447,7 @@ class HashExtTest < ActiveSupport::TestCase def test_update_with_to_hash_conversion hash = HashWithIndifferentAccess.new hash.update HashByConversion.new(a: 1) - assert_equal hash["a"], 1 + assert_equal 1, hash["a"] end def test_indifferent_merging @@ -472,7 +472,7 @@ class HashExtTest < ActiveSupport::TestCase def test_merge_with_to_hash_conversion hash = HashWithIndifferentAccess.new merged = hash.merge HashByConversion.new(a: 1) - assert_equal merged["a"], 1 + assert_equal 1, merged["a"] end def test_indifferent_replace @@ -536,11 +536,11 @@ class HashExtTest < ActiveSupport::TestCase def test_indifferent_deleting get_hash = proc { { a: "foo" }.with_indifferent_access } hash = get_hash.call - assert_equal hash.delete(:a), "foo" - assert_equal hash.delete(:a), nil + assert_equal "foo", hash.delete(:a) + assert_nil hash.delete(:a) hash = get_hash.call - assert_equal hash.delete("a"), "foo" - assert_equal hash.delete("a"), nil + assert_equal "foo", hash.delete("a") + assert_nil hash.delete("a") end def test_indifferent_select @@ -589,6 +589,16 @@ class HashExtTest < ActiveSupport::TestCase assert_instance_of ActiveSupport::HashWithIndifferentAccess, indifferent_strings end + def test_indifferent_compact + hash_contain_nil_value = @strings.merge("z" => nil) + hash = ActiveSupport::HashWithIndifferentAccess.new(hash_contain_nil_value) + compacted_hash = hash.compact + + assert_equal(@strings, compacted_hash) + assert_equal(hash_contain_nil_value, hash) + assert_instance_of ActiveSupport::HashWithIndifferentAccess, compacted_hash + end + def test_indifferent_to_hash # Should convert to a Hash with String keys. assert_equal @strings, @mixed.with_indifferent_access.to_hash @@ -933,8 +943,8 @@ class HashExtTest < ActiveSupport::TestCase extracted = original.extract!(:a, :x) assert_equal expected, extracted - assert_equal nil, extracted[:a] - assert_equal nil, extracted[:x] + assert_nil extracted[:a] + assert_nil extracted[:x] end def test_indifferent_extract @@ -1017,7 +1027,7 @@ class HashExtTest < ActiveSupport::TestCase assert_equal({}, h) h = @symbols.dup - assert_equal(nil, h.compact!) + assert_nil(h.compact!) assert_equal(@symbols, h) end @@ -1595,7 +1605,7 @@ class HashToXmlTest < ActiveSupport::TestCase def test_should_return_nil_if_no_key_is_supplied hash_wia = HashWithIndifferentAccess.new { 1 + 2 } - assert_equal nil, hash_wia.default + assert_nil hash_wia.default end def test_should_use_default_value_for_unknown_key diff --git a/activesupport/test/core_ext/module/attribute_accessor_per_thread_test.rb b/activesupport/test/core_ext/module/attribute_accessor_per_thread_test.rb index b816fa50e3..af240bc38d 100644 --- a/activesupport/test/core_ext/module/attribute_accessor_per_thread_test.rb +++ b/activesupport/test/core_ext/module/attribute_accessor_per_thread_test.rb @@ -121,11 +121,11 @@ class ModuleAttributeAccessorPerThreadTest < ActiveSupport::TestCase def test_should_not_affect_superclass_if_subclass_set_value @class.foo = "super" - assert_equal @class.foo, "super" + assert_equal "super", @class.foo assert_nil @subclass.foo @subclass.foo = "sub" - assert_equal @class.foo, "super" - assert_equal @subclass.foo, "sub" + assert_equal "super", @class.foo + assert_equal "sub", @subclass.foo end end diff --git a/activesupport/test/core_ext/module_test.rb b/activesupport/test/core_ext/module_test.rb index 8f2df22f27..a4515d1956 100644 --- a/activesupport/test/core_ext/module_test.rb +++ b/activesupport/test/core_ext/module_test.rb @@ -210,21 +210,21 @@ class ModuleTest < ActiveSupport::TestCase def test_delegation_prefix invoice = Invoice.new(@david) - assert_equal invoice.client_name, "David" - assert_equal invoice.client_street, "Paulina" - assert_equal invoice.client_city, "Chicago" + assert_equal "David", invoice.client_name + assert_equal "Paulina", invoice.client_street + assert_equal "Chicago", invoice.client_city end def test_delegation_custom_prefix invoice = Invoice.new(@david) - assert_equal invoice.customer_name, "David" - assert_equal invoice.customer_street, "Paulina" - assert_equal invoice.customer_city, "Chicago" + assert_equal "David", invoice.customer_name + assert_equal "Paulina", invoice.customer_street + assert_equal "Chicago", invoice.customer_city end def test_delegation_prefix_with_nil_or_false - assert_equal Developer.new(@david).name, "David" - assert_equal Tester.new(@david).name, "David" + assert_equal "David", Developer.new(@david).name + assert_equal "David", Tester.new(@david).name end def test_delegation_prefix_with_instance_variable @@ -240,7 +240,7 @@ class ModuleTest < ActiveSupport::TestCase def test_delegation_with_allow_nil rails = Project.new("Rails", Someone.new("David")) - assert_equal rails.name, "David" + assert_equal "David", rails.name end def test_delegation_with_allow_nil_and_nil_value diff --git a/activesupport/test/core_ext/object/blank_test.rb b/activesupport/test/core_ext/object/blank_test.rb index ab0676524e..1bedc76320 100644 --- a/activesupport/test/core_ext/object/blank_test.rb +++ b/activesupport/test/core_ext/object/blank_test.rb @@ -28,7 +28,7 @@ class BlankTest < ActiveSupport::TestCase end def test_presence - BLANK.each { |v| assert_equal nil, v.presence, "#{v.inspect}.presence should return nil" } + BLANK.each { |v| assert_nil v.presence, "#{v.inspect}.presence should return nil" } NOT.each { |v| assert_equal v, v.presence, "#{v.inspect}.presence should return self" } end end diff --git a/activesupport/test/core_ext/object/deep_dup_test.rb b/activesupport/test/core_ext/object/deep_dup_test.rb index e335ec1b40..f247ee16de 100644 --- a/activesupport/test/core_ext/object/deep_dup_test.rb +++ b/activesupport/test/core_ext/object/deep_dup_test.rb @@ -6,7 +6,7 @@ class DeepDupTest < ActiveSupport::TestCase array = [1, [2, 3]] dup = array.deep_dup dup[1][2] = 4 - assert_equal nil, array[1][2] + assert_nil array[1][2] assert_equal 4, dup[1][2] end @@ -14,7 +14,7 @@ class DeepDupTest < ActiveSupport::TestCase hash = { a: { b: "b" } } dup = hash.deep_dup dup[:a][:c] = "c" - assert_equal nil, hash[:a][:c] + assert_nil hash[:a][:c] assert_equal "c", dup[:a][:c] end @@ -22,7 +22,7 @@ class DeepDupTest < ActiveSupport::TestCase array = [1, { a: 2, b: 3 } ] dup = array.deep_dup dup[1][:c] = 4 - assert_equal nil, array[1][:c] + assert_nil array[1][:c] assert_equal 4, dup[1][:c] end @@ -30,7 +30,7 @@ class DeepDupTest < ActiveSupport::TestCase hash = { a: [1, 2] } dup = hash.deep_dup dup[:a][2] = "c" - assert_equal nil, hash[:a][2] + assert_nil hash[:a][2] assert_equal "c", dup[:a][2] end diff --git a/activesupport/test/core_ext/object/duplicable_test.rb b/activesupport/test/core_ext/object/duplicable_test.rb index c2a1e68f57..466f62a4b4 100644 --- a/activesupport/test/core_ext/object/duplicable_test.rb +++ b/activesupport/test/core_ext/object/duplicable_test.rb @@ -5,10 +5,10 @@ require "active_support/core_ext/numeric/time" class DuplicableTest < ActiveSupport::TestCase if RUBY_VERSION >= "2.4.0" - RAISE_DUP = [method(:puts)] - ALLOW_DUP = ["1", Object.new, /foo/, [], {}, Time.now, Class.new, Module.new, BigDecimal.new("4.56"), nil, false, true, :symbol, 1, 2.3] + RAISE_DUP = [method(:puts), Complex(1), Rational(1), "symbol_from_string".to_sym] + ALLOW_DUP = ["1", Object.new, /foo/, [], {}, Time.now, Class.new, Module.new, BigDecimal.new("4.56"), nil, false, true, 1, 2.3] else - RAISE_DUP = [nil, false, true, :symbol, 1, 2.3, method(:puts)] + RAISE_DUP = [nil, false, true, :symbol, 1, 2.3, method(:puts), Complex(1), Rational(1)] ALLOW_DUP = ["1", Object.new, /foo/, [], {}, Time.now, Class.new, Module.new, BigDecimal.new("4.56")] end diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb index 98bcdda45b..00685cd952 100644 --- a/activesupport/test/core_ext/string_ext_test.rb +++ b/activesupport/test/core_ext/string_ext_test.rb @@ -152,37 +152,37 @@ class StringInflectionsTest < ActiveSupport::TestCase def test_string_parameterized_normal StringToParameterized.each do |normal, slugged| - assert_equal(normal.parameterize, slugged) + assert_equal(slugged, normal.parameterize) end end def test_string_parameterized_normal_preserve_case StringToParameterizedPreserveCase.each do |normal, slugged| - assert_equal(normal.parameterize(preserve_case: true), slugged) + assert_equal(slugged, normal.parameterize(preserve_case: true)) end end def test_string_parameterized_no_separator StringToParameterizeWithNoSeparator.each do |normal, slugged| - assert_equal(normal.parameterize(separator: ""), slugged) + assert_equal(slugged, normal.parameterize(separator: "")) end end def test_string_parameterized_no_separator_preserve_case StringToParameterizePreserveCaseWithNoSeparator.each do |normal, slugged| - assert_equal(normal.parameterize(separator: "", preserve_case: true), slugged) + assert_equal(slugged, normal.parameterize(separator: "", preserve_case: true)) end end def test_string_parameterized_underscore StringToParameterizeWithUnderscore.each do |normal, slugged| - assert_equal(normal.parameterize(separator: "_"), slugged) + assert_equal(slugged, normal.parameterize(separator: "_")) end end def test_string_parameterized_underscore_preserve_case StringToParameterizePreserceCaseWithUnderscore.each do |normal, slugged| - assert_equal(normal.parameterize(separator: "_", preserve_case: true), slugged) + assert_equal(slugged, normal.parameterize(separator: "_", preserve_case: true)) end end @@ -283,7 +283,7 @@ class StringInflectionsTest < ActiveSupport::TestCase def test_truncate_words_with_complex_string Timeout.timeout(10) do complex_string = "aa aa aaa aa aaa aaa aaa aa aaa aaa aaa aaa aaa aaa aaa aaa aaa aaa aaaa aaaaa aaaaa aaaaaa aa aa aa aaa aa aaa aa aa aa aa a aaa aaa \n a aaa <<s" - assert_equal complex_string.truncate_words(80), complex_string + assert_equal complex_string, complex_string.truncate_words(80) end rescue Timeout::Error assert false @@ -339,7 +339,7 @@ class StringAccessTest < ActiveSupport::TestCase test "#at with Regex, returns the matching portion of the string" do assert_equal "lo", "hello".at(/lo/) - assert_equal nil, "hello".at(/nonexisting/) + assert_nil "hello".at(/nonexisting/) end test "#from with positive Integer, returns substring from the given position to the end" do @@ -710,14 +710,14 @@ class OutputSafetyTest < ActiveSupport::TestCase test "Prepending safe onto unsafe yields unsafe" do @string.prepend "other".html_safe assert !@string.html_safe? - assert_equal @string, "otherhello" + assert_equal "otherhello", @string end test "Prepending unsafe onto safe yields escaped safe" do other = "other".html_safe other.prepend "<foo>" assert other.html_safe? - assert_equal other, "<foo>other" + assert_equal "<foo>other", other end test "Concatting safe onto unsafe yields unsafe" do diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb index c11804c3dc..a399e36dc9 100644 --- a/activesupport/test/core_ext/time_ext_test.rb +++ b/activesupport/test/core_ext/time_ext_test.rb @@ -771,7 +771,7 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase assert_equal 1, Time.utc(2000) <=> Time.utc(1999, 12, 31, 23, 59, 59, 999).to_s assert_equal 0, Time.utc(2000) <=> Time.utc(2000, 1, 1, 0, 0, 0).to_s assert_equal(-1, Time.utc(2000) <=> Time.utc(2000, 1, 1, 0, 0, 1, 0).to_s) - assert_equal nil, Time.utc(2000) <=> "Invalid as Time" + assert_nil Time.utc(2000) <=> "Invalid as Time" end def test_at_with_datetime diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb index 620084f27d..629666947f 100644 --- a/activesupport/test/core_ext/time_with_zone_test.rb +++ b/activesupport/test/core_ext/time_with_zone_test.rb @@ -1057,7 +1057,7 @@ class TimeWithZoneMethodsForTimeAndDateTimeTest < ActiveSupport::TestCase Time.zone = -9.hours assert_equal ActiveSupport::TimeZone["Alaska"], Time.zone Time.zone = nil - assert_equal nil, Time.zone + assert_nil Time.zone end def test_time_zone_getter_and_setter_with_zone_default_set diff --git a/activesupport/test/dependencies_test.rb b/activesupport/test/dependencies_test.rb index f333a46d75..e772d15d53 100644 --- a/activesupport/test/dependencies_test.rb +++ b/activesupport/test/dependencies_test.rb @@ -121,7 +121,7 @@ class DependenciesTest < ActiveSupport::TestCase silence_warnings { require_dependency filename } assert_equal 2, $check_warnings_load_count - assert_equal nil, $checked_verbose, "After first load warnings should be left alone." + assert_nil $checked_verbose, "After first load warnings should be left alone." assert_includes ActiveSupport::Dependencies.loaded, expanded ActiveSupport::Dependencies.clear @@ -526,8 +526,8 @@ class DependenciesTest < ActiveSupport::TestCase def test_file_search with_loading "dependencies" do root = ActiveSupport::Dependencies.autoload_paths.first - assert_equal nil, ActiveSupport::Dependencies.search_for_file("service_three") - assert_equal nil, ActiveSupport::Dependencies.search_for_file("service_three.rb") + assert_nil ActiveSupport::Dependencies.search_for_file("service_three") + assert_nil ActiveSupport::Dependencies.search_for_file("service_three.rb") assert_equal root + "/service_one.rb", ActiveSupport::Dependencies.search_for_file("service_one") assert_equal root + "/service_one.rb", ActiveSupport::Dependencies.search_for_file("service_one.rb") end diff --git a/activesupport/test/descendants_tracker_test_cases.rb b/activesupport/test/descendants_tracker_test_cases.rb index 09c5ce1f07..cf349d53ee 100644 --- a/activesupport/test/descendants_tracker_test_cases.rb +++ b/activesupport/test/descendants_tracker_test_cases.rb @@ -40,7 +40,7 @@ module DescendantsTrackerTestCases end end - protected + private def assert_equal_sets(expected, actual) assert_equal Set.new(expected), Set.new(actual) diff --git a/activesupport/test/executor_test.rb b/activesupport/test/executor_test.rb index 03ec6020c3..7fefc066b3 100644 --- a/activesupport/test/executor_test.rb +++ b/activesupport/test/executor_test.rb @@ -105,7 +105,7 @@ class ExecutorTest < ActiveSupport::TestCase executor.wrap {} - assert_equal nil, supplied_state + assert_nil supplied_state end def test_exception_skips_uninvoked_hook diff --git a/activesupport/test/inflector_test.rb b/activesupport/test/inflector_test.rb index d881bd346d..8d39303f9b 100644 --- a/activesupport/test/inflector_test.rb +++ b/activesupport/test/inflector_test.rb @@ -31,6 +31,32 @@ class InflectorTest < ActiveSupport::TestCase assert_equal "", ActiveSupport::Inflector.pluralize("") end + test "uncountability of ascii word" do + word = "HTTP" + ActiveSupport::Inflector.inflections do |inflect| + inflect.uncountable word + end + + assert_equal word, ActiveSupport::Inflector.pluralize(word) + assert_equal word, ActiveSupport::Inflector.singularize(word) + assert_equal ActiveSupport::Inflector.pluralize(word), ActiveSupport::Inflector.singularize(word) + + ActiveSupport::Inflector.inflections.uncountables.pop + end + + test "uncountability of non-ascii word" do + word = "猫" + ActiveSupport::Inflector.inflections do |inflect| + inflect.uncountable word + end + + assert_equal word, ActiveSupport::Inflector.pluralize(word) + assert_equal word, ActiveSupport::Inflector.singularize(word) + assert_equal ActiveSupport::Inflector.pluralize(word), ActiveSupport::Inflector.singularize(word) + + ActiveSupport::Inflector.inflections.uncountables.pop + end + ActiveSupport::Inflector.inflections.uncountable.each do |word| define_method "test_uncountability_of_#{word}" do assert_equal word, ActiveSupport::Inflector.singularize(word) @@ -507,12 +533,4 @@ class InflectorTest < ActiveSupport::TestCase end end end - - def test_inflections_with_uncountable_words - ActiveSupport::Inflector.inflections do |inflect| - inflect.uncountable "HTTP" - end - - assert_equal "HTTP", ActiveSupport::Inflector.pluralize("HTTP") - end end diff --git a/activesupport/test/json/encoding_test.rb b/activesupport/test/json/encoding_test.rb index fa518104c2..6d8f7cfbd0 100644 --- a/activesupport/test/json/encoding_test.rb +++ b/activesupport/test/json/encoding_test.rb @@ -329,7 +329,7 @@ class TestJSONEncoding < ActiveSupport::TestCase end def test_nil_true_and_false_represented_as_themselves - assert_equal nil, nil.as_json + assert_nil nil.as_json assert_equal true, true.as_json assert_equal false, false.as_json end @@ -452,7 +452,7 @@ EXPECTED assert_equal '{"number":null}', NaNNumber.new.to_json end - protected + private def object_keys(json_object) json_object[1..-2].scan(/([^{}:,\s]+):/).flatten.sort diff --git a/activesupport/test/message_verifier_test.rb b/activesupport/test/message_verifier_test.rb index d56a46b250..d6109c761d 100644 --- a/activesupport/test/message_verifier_test.rb +++ b/activesupport/test/message_verifier_test.rb @@ -80,6 +80,6 @@ class MessageVerifierTest < ActiveSupport::TestCase exception = assert_raise(ArgumentError) do ActiveSupport::MessageVerifier.new(nil) end - assert_equal exception.message, "Secret should not be nil." + assert_equal "Secret should not be nil.", exception.message end end diff --git a/activesupport/test/multibyte_chars_test.rb b/activesupport/test/multibyte_chars_test.rb index 2ee4a1ce68..68ce42bf72 100644 --- a/activesupport/test/multibyte_chars_test.rb +++ b/activesupport/test/multibyte_chars_test.rb @@ -231,7 +231,7 @@ class MultibyteCharsUTF8BehaviourTest < ActiveSupport::TestCase assert_equal 0, @chars.index("こに") assert_equal 2, @chars.index("ち") assert_equal 2, @chars.index("ち", -2) - assert_equal nil, @chars.index("ち", -1) + assert_nil @chars.index("ち", -1) assert_equal 3, @chars.index("わ") assert_equal 5, "ééxééx".mb_chars.index("x", 4) end @@ -390,11 +390,11 @@ class MultibyteCharsUTF8BehaviourTest < ActiveSupport::TestCase end def test_slice_should_take_character_offsets - assert_equal nil, "".mb_chars.slice(0) + assert_nil "".mb_chars.slice(0) assert_equal "こ", @chars.slice(0) assert_equal "わ", @chars.slice(3) - assert_equal nil, "".mb_chars.slice(-1..1) - assert_equal nil, "".mb_chars.slice(-1, 1) + assert_nil "".mb_chars.slice(-1..1) + assert_nil "".mb_chars.slice(-1, 1) assert_equal "", "".mb_chars.slice(0..10) assert_equal "にちわ", @chars.slice(1..3) assert_equal "にちわ", @chars.slice(1, 3) @@ -403,10 +403,10 @@ class MultibyteCharsUTF8BehaviourTest < ActiveSupport::TestCase assert_equal "", @chars.slice(4..10) assert_equal "に", @chars.slice(/に/u) assert_equal "にち", @chars.slice(/に./u) - assert_equal nil, @chars.slice(/unknown/u) + assert_nil @chars.slice(/unknown/u) assert_equal "にち", @chars.slice(/(にち)/u, 1) - assert_equal nil, @chars.slice(/(にち)/u, 2) - assert_equal nil, @chars.slice(7..6) + assert_nil @chars.slice(/(にち)/u, 2) + assert_nil @chars.slice(7..6) end def test_slice_bang_returns_sliced_out_substring @@ -414,7 +414,7 @@ class MultibyteCharsUTF8BehaviourTest < ActiveSupport::TestCase end def test_slice_bang_returns_nil_on_out_of_bound_arguments - assert_equal nil, @chars.mb_chars.slice!(9..10) + assert_nil @chars.mb_chars.slice!(9..10) end def test_slice_bang_removes_the_slice_from_the_receiver diff --git a/activesupport/test/multibyte_conformance_test.rb b/activesupport/test/multibyte_conformance_test.rb index bf004d7924..ef1a26135f 100644 --- a/activesupport/test/multibyte_conformance_test.rb +++ b/activesupport/test/multibyte_conformance_test.rb @@ -82,7 +82,7 @@ class MultibyteConformanceTest < ActiveSupport::TestCase end end - protected + private def each_line_of_norm_tests(&block) File.open(File.join(CACHE_DIR, UNIDATA_FILE), "r") do | f | until f.eof? diff --git a/activesupport/test/multibyte_grapheme_break_conformance_test.rb b/activesupport/test/multibyte_grapheme_break_conformance_test.rb index 04a7d290d9..b3328987ae 100644 --- a/activesupport/test/multibyte_grapheme_break_conformance_test.rb +++ b/activesupport/test/multibyte_grapheme_break_conformance_test.rb @@ -28,7 +28,7 @@ class MultibyteGraphemeBreakConformanceTest < ActiveSupport::TestCase end end - protected + private def each_line_of_break_tests(&block) lines = 0 max_test_lines = 0 # Don't limit below 21, because that's the header of the testfile diff --git a/activesupport/test/multibyte_normalization_conformance_test.rb b/activesupport/test/multibyte_normalization_conformance_test.rb index e013bd578f..ebc9f92d23 100644 --- a/activesupport/test/multibyte_normalization_conformance_test.rb +++ b/activesupport/test/multibyte_normalization_conformance_test.rb @@ -83,7 +83,7 @@ class MultibyteNormalizationConformanceTest < ActiveSupport::TestCase end end - protected + private def each_line_of_norm_tests(&block) lines = 0 max_test_lines = 0 # Don't limit below 38, because that's the header of the testfile diff --git a/activesupport/test/notifications_test.rb b/activesupport/test/notifications_test.rb index a6f0d82e8a..11f743519f 100644 --- a/activesupport/test/notifications_test.rb +++ b/activesupport/test/notifications_test.rb @@ -273,7 +273,7 @@ module Notifications assert !not_child.parent_of?(parent) end - protected + private def random_id @random_id ||= SecureRandom.hex(10) end diff --git a/activesupport/test/safe_buffer_test.rb b/activesupport/test/safe_buffer_test.rb index e046b8d773..36c068b91f 100644 --- a/activesupport/test/safe_buffer_test.rb +++ b/activesupport/test/safe_buffer_test.rb @@ -175,6 +175,6 @@ class SafeBufferTest < ActiveSupport::TestCase test "Should not affect frozen objects when accessing characters" do x = "Hello".html_safe - assert_equal x[/a/, 1], nil + assert_nil x[/a/, 1] end end diff --git a/activesupport/test/test_case_test.rb b/activesupport/test/test_case_test.rb index d769a8c145..af7fc44d66 100644 --- a/activesupport/test/test_case_test.rb +++ b/activesupport/test/test_case_test.rb @@ -257,7 +257,7 @@ class SetupAndTeardownTest < ActiveSupport::TestCase def teardown end - protected + private def reset_callback_record @called_back = [] @@ -282,7 +282,7 @@ class SubclassSetupAndTeardownTest < SetupAndTeardownTest assert_equal [:foo, :sentinel, :bar], self.class._teardown_callbacks.map(&:raw_filter) end - protected + private def bar @called_back << :bar end diff --git a/activesupport/test/time_travel_test.rb b/activesupport/test/time_travel_test.rb index c68be329bc..cfc6447360 100644 --- a/activesupport/test/time_travel_test.rb +++ b/activesupport/test/time_travel_test.rb @@ -3,6 +3,10 @@ require "active_support/core_ext/date_time" require "active_support/core_ext/numeric/time" class TimeTravelTest < ActiveSupport::TestCase + class TimeSubclass < ::Time; end + class DateSubclass < ::Date; end + class DateTimeSubclass < ::DateTime; end + def test_time_helper_travel Time.stub(:now, Time.now) do begin @@ -142,4 +146,19 @@ class TimeTravelTest < ActiveSupport::TestCase end end end + + def test_time_helper_travel_with_time_subclass + assert_equal TimeSubclass, TimeSubclass.now.class + assert_equal DateSubclass, DateSubclass.today.class + assert_equal DateTimeSubclass, DateTimeSubclass.now.class + + travel 1.day do + assert_equal TimeSubclass, TimeSubclass.now.class + assert_equal DateSubclass, DateSubclass.today.class + assert_equal DateTimeSubclass, DateTimeSubclass.now.class + assert_equal Time.now.to_s, TimeSubclass.now.to_s + assert_equal Date.today.to_s, DateSubclass.today.to_s + assert_equal DateTime.now.to_s, DateTimeSubclass.now.to_s + end + end end diff --git a/activesupport/test/time_zone_test.rb b/activesupport/test/time_zone_test.rb index 914893ea10..4794b55742 100644 --- a/activesupport/test/time_zone_test.rb +++ b/activesupport/test/time_zone_test.rb @@ -416,7 +416,7 @@ class TimeZoneTest < ActiveSupport::TestCase def test_utc_offset_lazy_loaded_from_tzinfo_when_not_passed_in_to_initialize tzinfo = TZInfo::Timezone.get("America/New_York") zone = ActiveSupport::TimeZone.create(tzinfo.name, nil, tzinfo) - assert_equal nil, zone.instance_variable_get("@utc_offset") + assert_nil zone.instance_variable_get("@utc_offset") assert_equal(-18_000, zone.utc_offset) end diff --git a/guides/Rakefile b/guides/Rakefile index bf501f6a64..d2591f523c 100644 --- a/guides/Rakefile +++ b/guides/Rakefile @@ -13,8 +13,9 @@ namespace :guides do desc "Generate .mobi file. The kindlegen executable must be in your PATH. You can get it for free from http://www.amazon.com/gp/feature.html?docId=1000765211" task :kindle do - unless `kindlerb -v 2> /dev/null` =~ /kindlerb 1.0.1/ - abort "Please `gem install kindlerb` and make sure you have `kindlegen` in your PATH" + require "kindlerb" + unless Kindlerb.kindlegen_available? + abort "Please run `setupkindlerb` to install kindlegen" end unless `convert` =~ /convert/ abort "Please install ImageMagick`" diff --git a/guides/bug_report_templates/action_controller_master.rb b/guides/bug_report_templates/action_controller_master.rb index 486c7243ad..7644f6fe4a 100644 --- a/guides/bug_report_templates/action_controller_master.rb +++ b/guides/bug_report_templates/action_controller_master.rb @@ -8,6 +8,7 @@ end gemfile(true) do source "https://rubygems.org" gem "rails", github: "rails/rails" + gem "arel", github: "rails/arel" end require "action_controller/railtie" diff --git a/guides/bug_report_templates/active_job_master.rb b/guides/bug_report_templates/active_job_master.rb index f61518713f..7591470440 100644 --- a/guides/bug_report_templates/active_job_master.rb +++ b/guides/bug_report_templates/active_job_master.rb @@ -8,6 +8,7 @@ end gemfile(true) do source "https://rubygems.org" gem "rails", github: "rails/rails" + gem "arel", github: "rails/arel" end require "active_job" diff --git a/guides/bug_report_templates/active_record_master.rb b/guides/bug_report_templates/active_record_master.rb index 7265a671b0..8bbc1ef19e 100644 --- a/guides/bug_report_templates/active_record_master.rb +++ b/guides/bug_report_templates/active_record_master.rb @@ -8,6 +8,7 @@ end gemfile(true) do source "https://rubygems.org" gem "rails", github: "rails/rails" + gem "arel", github: "rails/arel" gem "sqlite3" end diff --git a/guides/bug_report_templates/active_record_migrations_master.rb b/guides/bug_report_templates/active_record_migrations_master.rb index 13a375d1ba..84a4b71909 100644 --- a/guides/bug_report_templates/active_record_migrations_master.rb +++ b/guides/bug_report_templates/active_record_migrations_master.rb @@ -8,6 +8,7 @@ end gemfile(true) do source "https://rubygems.org" gem "rails", github: "rails/rails" + gem "arel", github: "rails/arel" gem "sqlite3" end diff --git a/guides/bug_report_templates/benchmark.rb b/guides/bug_report_templates/benchmark.rb index 54433b34dd..a0b541d012 100644 --- a/guides/bug_report_templates/benchmark.rb +++ b/guides/bug_report_templates/benchmark.rb @@ -8,6 +8,7 @@ end gemfile(true) do source "https://rubygems.org" gem "rails", github: "rails/rails" + gem "arel", github: "rails/arel" gem "benchmark-ips" end diff --git a/guides/bug_report_templates/generic_master.rb b/guides/bug_report_templates/generic_master.rb index d3a7ae4ac4..ed45726e92 100644 --- a/guides/bug_report_templates/generic_master.rb +++ b/guides/bug_report_templates/generic_master.rb @@ -8,6 +8,7 @@ end gemfile(true) do source "https://rubygems.org" gem "rails", github: "rails/rails" + gem "arel", github: "rails/arel" end require "active_support" diff --git a/guides/rails_guides/kindle.rb b/guides/rails_guides/kindle.rb index 4b73ae9518..9536d0bd3b 100644 --- a/guides/rails_guides/kindle.rb +++ b/guides/rails_guides/kindle.rb @@ -1,9 +1,6 @@ #!/usr/bin/env ruby -unless `which kindlerb` - abort "Please gem install kindlerb" -end - +require "kindlerb" require "nokogiri" require "fileutils" require "yaml" @@ -28,10 +25,9 @@ module Kindle generate_document_metadata(mobi_outfile) puts "Creating MOBI document with kindlegen. This may take a while." - cmd = "kindlerb . > #{File.absolute_path logfile} 2>&1" - puts cmd - system(cmd) - puts "MOBI document generated at #{File.expand_path(mobi_outfile, output_dir)}" + if Kindlerb.run(output_dir) + puts "MOBI document generated at #{File.expand_path(mobi_outfile, output_dir)}" + end end end diff --git a/guides/source/5_0_release_notes.md b/guides/source/5_0_release_notes.md index 39753cbd6f..24fb0ca6e1 100644 --- a/guides/source/5_0_release_notes.md +++ b/guides/source/5_0_release_notes.md @@ -499,6 +499,9 @@ Please refer to the [Changelog][action-view] for detailed changes. `datetime-local`. ([Pull Request](https://github.com/rails/rails/pull/25469)) +* Allow blocks while rendering with the `render partial:` helper. + ([Pull Request](https://github.com/rails/rails/pull/17974)) + Action Mailer ------------- diff --git a/guides/source/action_cable_overview.md b/guides/source/action_cable_overview.md index 3716aa0ecb..319277ef68 100644 --- a/guides/source/action_cable_overview.md +++ b/guides/source/action_cable_overview.md @@ -62,7 +62,7 @@ module ApplicationCable self.current_user = find_verified_user end - protected + private def find_verified_user if current_user = User.find_by(id: cookies.signed[:user_id]) current_user diff --git a/guides/source/active_record_callbacks.md b/guides/source/active_record_callbacks.md index 2a1c960887..666d987f8c 100644 --- a/guides/source/active_record_callbacks.md +++ b/guides/source/active_record_callbacks.md @@ -36,7 +36,7 @@ class User < ApplicationRecord before_validation :ensure_login_has_a_value - protected + private def ensure_login_has_a_value if login.nil? self.login = email unless email.blank? @@ -66,7 +66,7 @@ class User < ApplicationRecord # :on takes an array as well after_validation :set_location, on: [ :create, :update ] - protected + private def normalize_name self.name = name.downcase.titleize end @@ -77,7 +77,7 @@ class User < ApplicationRecord end ``` -It is considered good practice to declare callback methods as protected or private. If left public, they can be called from outside of the model and violate the principle of object encapsulation. +It is considered good practice to declare callback methods as private. If left public, they can be called from outside of the model and violate the principle of object encapsulation. Available Callbacks ------------------- @@ -202,11 +202,9 @@ The following methods trigger callbacks: * `create` * `create!` -* `decrement!` * `destroy` * `destroy!` * `destroy_all` -* `increment!` * `save` * `save!` * `save(validate: false)` diff --git a/guides/source/active_record_postgresql.md b/guides/source/active_record_postgresql.md index 58af2f82b3..6d07291b07 100644 --- a/guides/source/active_record_postgresql.md +++ b/guides/source/active_record_postgresql.md @@ -111,7 +111,7 @@ profile.settings = {"color" => "yellow", "resolution" => "1280x1024"} profile.save! Profile.where("settings->'color' = ?", "yellow") -#=> #<ActiveRecord::Relation [#<Profile id: 1, settings: {"color"=>"yellow", "resolution"=>"1280x1024"}>]> +# => #<ActiveRecord::Relation [#<Profile id: 1, settings: {"color"=>"yellow", "resolution"=>"1280x1024"}>]> ``` ### JSON diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md index 6bbc79a326..67bed4c8da 100644 --- a/guides/source/active_support_core_extensions.md +++ b/guides/source/active_support_core_extensions.md @@ -135,36 +135,53 @@ NOTE: Defined in `active_support/core_ext/object/blank.rb`. ### `duplicable?` -A few fundamental objects in Ruby are singletons. For example, in the whole life of a program the integer 1 refers always to the same instance: +In Ruby 2.4 most objects can be duplicated via `dup` or `clone` except +methods and certain numbers. Though Ruby 2.2 and 2.3 can't duplicate `nil`, +`false`, `true`, and symbols as well as instances `Float`, `Fixnum`, +and `Bignum` instances. ```ruby -1.object_id # => 3 -Math.cos(0).to_i.object_id # => 3 +"foo".dup # => "foo" +"".dup # => "" +1.method(:+).dup # => TypeError: allocator undefined for Method +Complex(0).dup # => TypeError: can't copy Complex ``` -Hence, there's no way these objects can be duplicated through `dup` or `clone`: +Active Support provides `duplicable?` to query an object about this: ```ruby -true.dup # => TypeError: can't dup TrueClass +"foo".duplicable? # => true +"".duplicable? # => true +Rational(1).duplicable? # => false +Complex(1).duplicable? # => false +1.method(:+).duplicable? # => false ``` -Some numbers which are not singletons are not duplicable either: +`duplicable?` matches Ruby's `dup` according to the Ruby version. + +So in 2.4: ```ruby -0.0.clone # => allocator undefined for Float -(2**1024).clone # => allocator undefined for Bignum +nil.dup # => nil +:my_symbol.dup # => :my_symbol +1.dup # => 1 + +nil.duplicable? # => true +:my_symbol.duplicable? # => true +1.duplicable? # => true ``` -Active Support provides `duplicable?` to programmatically query an object about this property: +Whereas in 2.2 and 2.3: ```ruby -"foo".duplicable? # => true -"".duplicable? # => true -0.0.duplicable? # => false -false.duplicable? # => false -``` +nil.dup # => TypeError: can't dup NilClass +:my_symbol.dup # => TypeError: can't dup Symbol +1.dup # => TypeError: can't dup Fixnum -By definition all objects are `duplicable?` except `nil`, `false`, `true`, symbols, numbers, class, module, and method objects. +nil.duplicable? # => false +:my_symbol.duplicable? # => false +1.duplicable? # => false +``` WARNING: Any class can disallow duplication by removing `dup` and `clone` or raising exceptions from them. Thus only `rescue` can tell whether a given arbitrary object is duplicable. `duplicable?` depends on the hard-coded list above, but it is much faster than `rescue`. Use it only if you know the hard-coded list is enough in your use case. diff --git a/guides/source/asset_pipeline.md b/guides/source/asset_pipeline.md index 25717e04e4..360de9a584 100644 --- a/guides/source/asset_pipeline.md +++ b/guides/source/asset_pipeline.md @@ -78,9 +78,9 @@ requests can mean faster loading for your application. Sprockets concatenates all JavaScript files into one master `.js` file and all CSS files into one master `.css` file. As you'll learn later in this guide, you can customize this strategy to group files any way you like. In production, -Rails inserts an MD5 fingerprint into each filename so that the file is cached -by the web browser. You can invalidate the cache by altering this fingerprint, -which happens automatically whenever you change the file contents. +Rails inserts an SHA256 fingerprint into each filename so that the file is +cached by the web browser. You can invalidate the cache by altering this +fingerprint, which happens automatically whenever you change the file contents. The second feature of the asset pipeline is asset minification or compression. For CSS files, this is done by removing whitespace and comments. For JavaScript, @@ -106,7 +106,7 @@ or in web browsers) to keep their own copy of the content. When the content is updated, the fingerprint will change. This will cause the remote clients to request a new copy of the content. This is generally known as _cache busting_. -The technique sprockets uses for fingerprinting is to insert a hash of the +The technique Sprockets uses for fingerprinting is to insert a hash of the content into the name, usually at the end. For example a CSS file `global.css` ``` @@ -346,9 +346,9 @@ Provided that the pipeline is enabled within your application (and not disabled in the current environment context), this file is served by Sprockets. If a file exists at `public/assets/rails.png` it is served by the web server. -Alternatively, a request for a file with an MD5 hash such as -`public/assets/rails-af27b6a414e6da00003503148be9b409.png` is treated the same -way. How these hashes are generated is covered in the [In +Alternatively, a request for a file with an SHA256 hash such as +`public/assets/rails-f90d8a84c707a8dc923fca1ca1895ae8ed0a09237f6992015fef1e11be77c023.png` +is treated the same way. How these hashes are generated is covered in the [In Production](#in-production) section later on in this guide. Sprockets will also look through the paths specified in `config.assets.paths`, @@ -654,7 +654,7 @@ In the production environment Sprockets uses the fingerprinting scheme outlined above. By default Rails assumes assets have been precompiled and will be served as static assets by your web server. -During the precompilation phase an MD5 is generated from the contents of the +During the precompilation phase an SHA256 is generated from the contents of the compiled files, and inserted into the filenames as they are written to disk. These fingerprinted names are used by the Rails helpers in place of the manifest name. @@ -743,22 +743,24 @@ Rails.application.config.assets.precompile += %w( admin.js admin.css ) NOTE. Always specify an expected compiled filename that ends with .js or .css, even if you want to add Sass or CoffeeScript files to the precompile array. -The task also generates a `manifest-md5hash.json` that contains a list with -all your assets and their respective fingerprints. This is used by the Rails -helper methods to avoid handing the mapping requests back to Sprockets. A -typical manifest file looks like: +The task also generates a `.sprockets-manifest-md5hash.json` (where `md5hash` is +an MD5 hash) that contains a list with all your assets and their respective +fingerprints. This is used by the Rails helper methods to avoid handing the +mapping requests back to Sprockets. A typical manifest file looks like: ```ruby -{"files":{"application-723d1be6cc741a3aabb1cec24276d681.js":{"logical_path":"application.js","mtime":"2013-07-26T22:55:03-07:00","size":302506, -"digest":"723d1be6cc741a3aabb1cec24276d681"},"application-12b3c7dd74d2e9df37e7cbb1efa76a6d.css":{"logical_path":"application.css","mtime":"2013-07-26T22:54:54-07:00","size":1560, -"digest":"12b3c7dd74d2e9df37e7cbb1efa76a6d"},"application-1c5752789588ac18d7e1a50b1f0fd4c2.css":{"logical_path":"application.css","mtime":"2013-07-26T22:56:17-07:00","size":1591, -"digest":"1c5752789588ac18d7e1a50b1f0fd4c2"},"favicon-a9c641bf2b81f0476e876f7c5e375969.ico":{"logical_path":"favicon.ico","mtime":"2013-07-26T23:00:10-07:00","size":1406, -"digest":"a9c641bf2b81f0476e876f7c5e375969"},"my_image-231a680f23887d9dd70710ea5efd3c62.png":{"logical_path":"my_image.png","mtime":"2013-07-26T23:00:27-07:00","size":6646, -"digest":"231a680f23887d9dd70710ea5efd3c62"}},"assets":{"application.js": -"application-723d1be6cc741a3aabb1cec24276d681.js","application.css": -"application-1c5752789588ac18d7e1a50b1f0fd4c2.css", -"favicon.ico":"favicona9c641bf2b81f0476e876f7c5e375969.ico","my_image.png": -"my_image-231a680f23887d9dd70710ea5efd3c62.png"}} +{"files":{"application-aee4be71f1288037ae78b997df388332edfd246471b533dcedaa8f9fe156442b.js":{"logical_path":"application.js","mtime":"2016-12-23T20:12:03-05:00","size":412383, +"digest":"aee4be71f1288037ae78b997df388332edfd246471b533dcedaa8f9fe156442b","integrity":"sha256-ruS+cfEogDeueLmX3ziDMu39JGRxtTPc7aqPn+FWRCs="}, +"application-86a292b5070793c37e2c0e5f39f73bb387644eaeada7f96e6fc040a028b16c18.css":{"logical_path":"application.css","mtime":"2016-12-23T19:12:20-05:00","size":2994, +"digest":"86a292b5070793c37e2c0e5f39f73bb387644eaeada7f96e6fc040a028b16c18","integrity":"sha256-hqKStQcHk8N+LA5fOfc7s4dkTq6tp/lub8BAoCixbBg="}, +"favicon-8d2387b8d4d32cecd93fa3900df0e9ff89d01aacd84f50e780c17c9f6b3d0eda.ico":{"logical_path":"favicon.ico","mtime":"2016-12-23T20:11:00-05:00","size":8629, +"digest":"8d2387b8d4d32cecd93fa3900df0e9ff89d01aacd84f50e780c17c9f6b3d0eda","integrity":"sha256-jSOHuNTTLOzZP6OQDfDp/4nQGqzYT1DngMF8n2s9Dto="}, +"my_image-f4028156fd7eca03584d5f2fc0470df1e0dbc7369eaae638b2ff033f988ec493.png":{"logical_path":"my_image.png","mtime":"2016-12-23T20:10:54-05:00","size":23414, +"digest":"f4028156fd7eca03584d5f2fc0470df1e0dbc7369eaae638b2ff033f988ec493","integrity":"sha256-9AKBVv1+ygNYTV8vwEcN8eDbxzaequY4sv8DP5iOxJM="}}, +"assets":{"application.js":"application-aee4be71f1288037ae78b997df388332edfd246471b533dcedaa8f9fe156442b.js", +"application.css":"application-86a292b5070793c37e2c0e5f39f73bb387644eaeada7f96e6fc040a028b16c18.css", +"favicon.ico":"favicon-8d2387b8d4d32cecd93fa3900df0e9ff89d01aacd84f50e780c17c9f6b3d0eda.ico", +"my_image.png":"my_image-f4028156fd7eca03584d5f2fc0470df1e0dbc7369eaae638b2ff033f988ec493.png"}} ``` The default location for the manifest is the root of the location specified in @@ -850,7 +852,7 @@ config.assets.compile = true On the first request the assets are compiled and cached as outlined in development above, and the manifest names used in the helpers are altered to -include the MD5 hash. +include the SHA256 hash. Sprockets also sets the `Cache-Control` HTTP header to `max-age=31536000`. This signals all caches between your server and the client browser that this content diff --git a/guides/source/association_basics.md b/guides/source/association_basics.md index 03d3daecc8..6e68935f9b 100644 --- a/guides/source/association_basics.md +++ b/guides/source/association_basics.md @@ -709,55 +709,73 @@ class Book < ApplicationRecord end ``` -By default, Active Record doesn't know about the connection between these associations. This can lead to two copies of an object getting out of sync: +Active Record will attempt to automatically identify that these two models share a bi-directional association based on the association name. In this way, Active Record will only load one copy of the `Author` object, making your application more efficient and preventing inconsistent data: ```ruby a = Author.first b = a.books.first a.first_name == b.author.first_name # => true -a.first_name = 'Manny' -a.first_name == b.author.first_name # => false +a.first_name = 'David' +a.first_name == b.author.first_name # => true ``` -This happens because `a` and `b.author` are two different in-memory representations of the same data, and neither one is automatically refreshed from changes to the other. Active Record provides the `:inverse_of` option so that you can inform it of these relations: +Active Record supports automatic identification for most associations with standard names. However, Active Record will not automatically identify bi-directional associations that contain any of the following options: + +* `:conditions` +* `:through` +* `:polymorphic` +* `:class_name` +* `:foreign_key` + +For example, consider the following model declarations: ```ruby class Author < ApplicationRecord - has_many :books, inverse_of: :author + has_many :books end class Book < ApplicationRecord - belongs_to :author, inverse_of: :books + belongs_to :writer, class_name: 'Author', foreign_key: 'author_id' end ``` -With these changes, Active Record will only load one copy of the author object, preventing inconsistencies and making your application more efficient: +Active Record will no longer automatically recognize the bi-directional association: ```ruby a = Author.first b = a.books.first -a.first_name == b.author.first_name # => true -a.first_name = 'Manny' -a.first_name == b.author.first_name # => true +a.first_name == b.writer.first_name # => true +a.first_name = 'David' +a.first_name == b.writer.first_name # => false +``` + +Active Record provides the `:inverse_of` option so you can explicitly declare bi-directional associations: + +```ruby +class Author < ApplicationRecord + has_many :books, inverse_of: 'writer' +end + +class Book < ApplicationRecord + belongs_to :writer, class_name: 'Author', foreign_key: 'author_id' +end ``` -There are a few limitations to `inverse_of` support: +By including the `:inverse_of` option in the `has_many` association declaration, Active Record will now recognize the bi-directional association: + +```ruby +a = Author.first +b = a.books.first +a.first_name == b.writer.first_name # => true +a.first_name = 'David' +a.first_name == b.writer.first_name # => true +``` + +There are a few limitations to `:inverse_of` support: * They do not work with `:through` associations. * They do not work with `:polymorphic` associations. * They do not work with `:as` associations. -* For `belongs_to` associations, `has_many` inverse associations are ignored. - -Every association will attempt to automatically find the inverse association -and set the `:inverse_of` option heuristically (based on the association name). -Most associations with standard names will be supported. However, associations -that contain the following options will not have their inverses set -automatically: - -* `:conditions` -* `:through` -* `:polymorphic` -* `:foreign_key` Detailed Association Reference ------------------------------ diff --git a/guides/source/command_line.md b/guides/source/command_line.md index 9d7ecce947..c8d559745e 100644 --- a/guides/source/command_line.md +++ b/guides/source/command_line.md @@ -407,8 +407,8 @@ db:fixtures:load Loads fixtures into the ... db:migrate Migrate the database ... db:migrate:status Display status of migrations db:rollback Rolls the schema back to ... -db:schema:cache:clear Clears a db/schema_cache.dump file -db:schema:cache:dump Creates a db/schema_cache.dump file +db:schema:cache:clear Clears a db/schema_cache.yml file +db:schema:cache:dump Creates a db/schema_cache.yml file db:schema:dump Creates a db/schema.rb file ... db:schema:load Loads a schema.rb file ... db:seed Loads the seed data ... diff --git a/guides/source/generators.md b/guides/source/generators.md index 32bbdc554a..d0b6cef3fd 100644 --- a/guides/source/generators.md +++ b/guides/source/generators.md @@ -208,7 +208,15 @@ $ bin/rails generate scaffold User name:string Looking at this output, it's easy to understand how generators work in Rails 3.0 and above. The scaffold generator doesn't actually generate anything, it just invokes others to do the work. This allows us to add/replace/remove any of those invocations. For instance, the scaffold generator invokes the scaffold_controller generator, which invokes erb, test_unit and helper generators. Since each generator has a single responsibility, they are easy to reuse, avoiding code duplication. -Our first customization on the workflow will be to stop generating stylesheet, JavaScript and test fixture files for scaffolds. We can achieve that by changing our configuration to the following: +If we want to avoid generating the default `app/assets/stylesheets/scaffolds.scss` file when scaffolding a new resource we can disable `scaffold_stylesheet`: + +```ruby + config.generators do |g| + g.scaffold_stylesheet false + end +``` + +The next customization on the workflow will be to stop generating stylesheet, JavaScript and test fixture files for scaffolds altogether. We can achieve that by changing our configuration to the following: ```ruby config.generators do |g| @@ -451,6 +459,26 @@ $ rails new thud -m https://gist.github.com/radar/722911/raw/ Whilst the final section of this guide doesn't cover how to generate the most awesome template known to man, it will take you through the methods available at your disposal so that you can develop it yourself. These same methods are also available for generators. +Adding Command Line Arguments +----------------------------- +Rails generators can be easily modified to accept custom command line arguments. This functionality comes from [Thor](http://www.rubydoc.info/github/erikhuda/thor/master/Thor/Base/ClassMethods#class_option-instance_method): + +``` +class_option :scope, type: :string, default: 'read_products' +``` + +Now our generator can be invoked as follows: + +```bash +rails generate initializer --scope write_products +``` + +The command line arguments are accessed through the `options` method inside the generator class. e.g: + +```ruby +@scope = options['scope'] +``` + Generator methods ----------------- diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md index 6ec5106bb3..8a451ab793 100644 --- a/guides/source/getting_started.md +++ b/guides/source/getting_started.md @@ -474,7 +474,7 @@ one here because the `ArticlesController` inherits from `ApplicationController`. The next part of the message contains `request.formats` which specifies the format of template to be served in response. It is set to `text/html` as we requested this page via browser, so Rails is looking for an HTML template. -`request.variants` specifies what kind of physical devices would be served by +`request.variant` specifies what kind of physical devices would be served by the response and helps Rails determine which template to use in the response. It is empty because no information has been provided. @@ -827,7 +827,7 @@ NOTE: A frequent practice is to place the standard CRUD actions in each controller in the following order: `index`, `show`, `new`, `edit`, `create`, `update` and `destroy`. You may use any order you choose, but keep in mind that these are public methods; as mentioned earlier in this guide, they must be placed -before any private or protected method in the controller in order to work. +before declaring `private` visibility in the controller. Given that, let's add the `show` action, as follows: diff --git a/guides/source/i18n.md b/guides/source/i18n.md index fd54bca4ff..df5869f9ca 100644 --- a/guides/source/i18n.md +++ b/guides/source/i18n.md @@ -72,11 +72,13 @@ I18n.l Time.now There are also attribute readers and writers for the following attributes: ```ruby -load_path # Announce your custom translation files -locale # Get and set the current locale -default_locale # Get and set the default locale -exception_handler # Use a different exception_handler -backend # Use a different backend +load_path # Announce your custom translation files +locale # Get and set the current locale +default_locale # Get and set the default locale +available_locales # Whitelist locales available for the application +enforce_available_locales # Enforce locale whitelisting (true or false) +exception_handler # Use a different exception_handler +backend # Use a different backend ``` So, let's internationalize a simple Rails application from the ground up in the next chapters! @@ -124,6 +126,9 @@ The load path must be specified before any translations are looked up. To change # Where the I18n library should search for translation files I18n.load_path += Dir[Rails.root.join('lib', 'locale', '*.{rb,yml}')] +# Whitelist locales available for the application +I18n.available_locales = [:en, :pt] + # Set default locale to something other than :en I18n.default_locale = :pt ``` diff --git a/guides/source/initialization.md b/guides/source/initialization.md index 57ed35d0d8..aa7bbcc19b 100644 --- a/guides/source/initialization.md +++ b/guides/source/initialization.md @@ -74,7 +74,7 @@ This file is as follows: ```ruby #!/usr/bin/env ruby -APP_PATH = File.expand_path('../../config/application', __FILE__) +APP_PATH = File.expand_path('../config/application', __dir__) require_relative '../config/boot' require 'rails/commands' ``` @@ -86,7 +86,7 @@ The `APP_PATH` constant will be used later in `rails/commands`. The `config/boot `config/boot.rb` contains: ```ruby -ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) require 'bundler/setup' # Set up gems listed in the Gemfile. ``` @@ -131,7 +131,7 @@ Once `config/boot.rb` has finished, the next file that is required is `ARGV` array simply contains `server` which will be passed over: ```ruby -ARGV << '--help' if ARGV.empty? +require "rails/command" aliases = { "g" => "generate", @@ -146,33 +146,37 @@ aliases = { command = ARGV.shift command = aliases[command] || command -require 'rails/commands/commands_tasks' - -Rails::CommandsTasks.new(ARGV).run_command!(command) +Rails::Command.invoke command, ARGV ``` -TIP: As you can see, an empty ARGV list will make Rails show the help -snippet. - If we had used `s` rather than `server`, Rails would have used the `aliases` defined here to find the matching command. -### `rails/commands/commands_tasks.rb` +### `rails/command.rb` -When one types a valid Rails command, `run_command!` a method of the same name -is called. If Rails doesn't recognize the command, it tries to run a Rake task -of the same name. +When one types a Rails command, `invoke` tries to lookup a command for the given +namespace and executing the command if found. -```ruby -COMMAND_WHITELIST = %w(plugin generate destroy console server dbconsole application runner new version help) +If Rails doesn't recognize the command, it hands the reins over to Rake +to run a task of the same name. -def run_command!(command) - command = parse_command(command) +As shown, `Rails::Command` displays the help output automatically if the `args` +are empty. - if COMMAND_WHITELIST.include?(command) - send(command) - else - run_rake_task(command) +```ruby +module Rails::Command + class << self + def invoke(namespace, args = [], **config) + namespace = namespace.to_s + namespace = "help" if namespace.blank? || Thor::HELP_MAPPINGS.include?(namespace) + namespace = "version" if %w( -v --version ).include? namespace + + if command = find_by_namespace(namespace) + command.perform(namespace, args, config) + else + find_by_namespace("rake").perform(namespace, args, config) + end + end end end ``` @@ -180,53 +184,39 @@ end With the `server` command, Rails will further run the following code: ```ruby -def set_application_directory! - Dir.chdir(File.expand_path('../../', APP_PATH)) unless File.exist?(File.expand_path("config.ru")) -end - -def server - set_application_directory! - require_command!("server") - - Rails::Server.new.tap do |server| - # We need to require application after the server sets environment, - # otherwise the --environment option given to the server won't propagate. - require APP_PATH - Dir.chdir(Rails.application.root) - server.start +module Rails + module Command + class ServerCommand < Base # :nodoc: + def perform + set_application_directory! + + Rails::Server.new.tap do |server| + # Require application after server sets environment to propagate + # the --environment option. + require APP_PATH + Dir.chdir(Rails.application.root) + server.start + end + end + end end end - -def require_command!(command) - require "rails/commands/#{command}" -end ``` This file will change into the Rails root directory (a path two directories up from `APP_PATH` which points at `config/application.rb`), but only if the -`config.ru` file isn't found. This then requires `rails/commands/server` which -sets up the `Rails::Server` class. - -```ruby -require 'fileutils' -require 'optparse' -require 'action_dispatch' -require 'rails' - -module Rails - class Server < ::Rack::Server -``` - -`fileutils` and `optparse` are standard Ruby libraries which provide helper functions for working with files and parsing options. +`config.ru` file isn't found. This then starts up the `Rails::Server` class. ### `actionpack/lib/action_dispatch.rb` Action Dispatch is the routing component of the Rails framework. It adds functionality like routing, session, and common middlewares. -### `rails/commands/server.rb` +### `rails/commands/server/server_command.rb` -The `Rails::Server` class is defined in this file by inheriting from `Rack::Server`. When `Rails::Server.new` is called, this calls the `initialize` method in `rails/commands/server.rb`: +The `Rails::Server` class is defined in this file by inheriting from +`Rack::Server`. When `Rails::Server.new` is called, this calls the `initialize` +method in `rails/commands/server/server_command.rb`: ```ruby def initialize(*) @@ -252,7 +242,10 @@ end In this case, `options` will be `nil` so nothing happens in this method. -After `super` has finished in `Rack::Server`, we jump back to `rails/commands/server.rb`. At this point, `set_environment` is called within the context of the `Rails::Server` object and this method doesn't appear to do much at first glance: +After `super` has finished in `Rack::Server`, we jump back to +`rails/commands/server/server_command.rb`. At this point, `set_environment` +is called within the context of the `Rails::Server` object and this method +doesn't appear to do much at first glance: ```ruby def set_environment @@ -289,17 +282,15 @@ With the `default_options` set to this: ```ruby def default_options - environment = ENV['RACK_ENV'] || 'development' - default_host = environment == 'development' ? 'localhost' : '0.0.0.0' - - { - :environment => environment, - :pid => nil, - :Port => 9292, - :Host => default_host, - :AccessLog => [], - :config => "config.ru" - } + super.merge( + Port: ENV.fetch("PORT", 3000).to_i, + Host: ENV.fetch("HOST", "localhost").dup, + DoNotReverseLookup: true, + environment: (ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development").dup, + daemonize: false, + caching: nil, + pid: Options::DEFAULT_PID_PATH, + restart_cmd: restart_command) end ``` @@ -311,22 +302,25 @@ def opt_parser end ``` -The class **is** defined in `Rack::Server`, but is overwritten in `Rails::Server` to take different arguments. Its `parse!` method begins like this: +The class **is** defined in `Rack::Server`, but is overwritten in +`Rails::Server` to take different arguments. Its `parse!` method looks +like this: ```ruby def parse!(args) args, options = args.dup, {} - opt_parser = OptionParser.new do |opts| - opts.banner = "Usage: rails server [puma, thin, etc] [options]" - opts.on("-p", "--port=port", Integer, - "Runs Rails on the specified port.", "Default: 3000") { |v| options[:Port] = v } - ... + option_parser(options).parse! args + + options[:log_stdout] = options[:daemonize].blank? && (options[:environment] || Rails.env) == "development" + options[:server] = args.shift + options +end ``` This method will set up keys for the `options` which Rails will then be able to use to determine how its server should run. After `initialize` -has finished, we jump back into `rails/server` where `APP_PATH` (which was +has finished, we jump back into the server command where `APP_PATH` (which was set earlier) is required. ### `config/application` @@ -345,6 +339,7 @@ def start print_boot_information trap(:INT) { exit } create_tmp_directories + setup_dev_caching log_to_stdout if options[:log_stdout] super @@ -352,7 +347,6 @@ def start end private - def print_boot_information ... puts "=> Run `rails server -h` for more startup options" @@ -364,21 +358,30 @@ private end end + def setup_dev_caching + if options[:environment] == "development" + Rails::DevCaching.enable_by_argument(options[:caching]) + end + end + def log_to_stdout wrapped_app # touch the app so the logger is set up - - console = ActiveSupport::Logger.new($stdout) + + console = ActiveSupport::Logger.new(STDOUT) console.formatter = Rails.logger.formatter console.level = Rails.logger.level - - Rails.logger.extend(ActiveSupport::Logger.broadcast(console)) + + unless ActiveSupport::Logger.logger_outputs_to?(Rails.logger, STDOUT) + Rails.logger.extend(ActiveSupport::Logger.broadcast(console)) + end end ``` This is where the first output of the Rails initialization happens. This method creates a trap for `INT` signals, so if you `CTRL-C` the server, it will exit the process. As we can see from the code here, it will create the `tmp/cache`, -`tmp/pids`, and `tmp/sockets` directories. It then calls `wrapped_app` which is +`tmp/pids`, and `tmp/sockets` directories. It then enables caching in development +if `rails server` is called with `--dev-caching`. Finally, it calls `wrapped_app` which is responsible for creating the Rack app, before creating and assigning an instance of `ActiveSupport::Logger`. @@ -538,7 +541,7 @@ require "rails" sprockets/railtie ).each do |railtie| begin - require "#{railtie}" + require railtie rescue LoadError end end diff --git a/guides/source/routing.md b/guides/source/routing.md index 937e313663..86492a9332 100644 --- a/guides/source/routing.md +++ b/guides/source/routing.md @@ -603,6 +603,14 @@ get 'photos/:id', to: 'photos#show', defaults: { format: 'jpg' } Rails would match `photos/12` to the `show` action of `PhotosController`, and set `params[:format]` to `"jpg"`. +You can also use `defaults` in a block format to define the defaults for multiple items: + +```ruby +defaults format: :json do + resources :photos +end +``` + NOTE: You cannot override defaults via query parameters - this is for security reasons. The only defaults that can be overridden are dynamic segments via substitution in the URL path. ### Naming Routes diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md index dda2b12a3a..8a3b3b84b4 100644 --- a/guides/source/upgrading_ruby_on_rails.md +++ b/guides/source/upgrading_ruby_on_rails.md @@ -1278,6 +1278,10 @@ Also check your environment settings for `config.action_dispatch.best_standards_ Rails 4.0 removes the `j` alias for `ERB::Util#json_escape` since `j` is already used for `ActionView::Helpers::JavaScriptHelper#escape_javascript`. +#### Cache + +The caching method changed between Rails 3.x and 4.0. You should [change the cache namespace](http://guides.rubyonrails.org/caching_with_rails.html#activesupport-cache-store) and roll out with a cold cache. + ### Helpers Loading Order The order in which helpers from more than one directory are loaded has changed in Rails 4.0. Previously, they were gathered and then sorted alphabetically. After upgrading to Rails 4.0, helpers will preserve the order of loaded directories and will be sorted alphabetically only within each directory. Unless you explicitly use the `helpers_path` parameter, this change will only impact the way of loading helpers from engines. If you rely on the ordering, you should check if correct methods are available after upgrade. If you would like to change the order in which engines are loaded, you can use `config.railties_order=` method. diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md index 8a1a440fca..6cb88ca01d 100644 --- a/railties/CHANGELOG.md +++ b/railties/CHANGELOG.md @@ -1,13 +1,48 @@ +* Don't generate HTML/ERB templates for scaffold controller with `--api` flag. + + Fixes #27591. + + *Prathamesh Sonpatki* + +* Make `Rails.env` fall back to `development` when `RAILS_ENV` and `RACK_ENV` is an empty string. + + *Daniel Deng* + +* Remove deprecated `CONTROLLER` environment variable for `routes` task. + + *Rafael Mendonça França* + +* Remove deprecated tasks: `rails:update`, `rails:template`, `rails:template:copy`, + `rails:update:configs` and `rails:update:bin`. + + *Rafael Mendonça França* + +* Remove deprecated file `rails/rack/debugger`. + + *Rafael Mendonça França* + +* Remove deprecated `config.serve_static_files`. + + *Rafael Mendonça França* + +* Remove deprecated `config.static_cache_control`. + + *Rafael Mendonça França* + +* The `log:clear` task clear all environments log files by default. + + *Yuji Yaginuma* + * Add Webpack support in new apps via the --webpack option, which will delegate to the rails/webpacker gem. To generate a new app that has Webpack dependencies configured and binstubs for webpack and webpack-watcher: - rails new myapp --webpack + `rails new myapp --webpack` To generate a new app that has Webpack + React configured and an example intalled: - - rails new myapp --webpack=react - + + `rails new myapp --webpack=react` + *DHH* * Add Yarn support in new apps with a yarn binstub and vendor/package.json. Skippable via --skip-yarn option. diff --git a/railties/MIT-LICENSE b/railties/MIT-LICENSE index 1f496cf280..f9e4444f07 100644 --- a/railties/MIT-LICENSE +++ b/railties/MIT-LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2016 David Heinemeier Hansson +Copyright (c) 2004-2017 David Heinemeier Hansson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/railties/bin/test b/railties/bin/test new file mode 100755 index 0000000000..a7beb14b27 --- /dev/null +++ b/railties/bin/test @@ -0,0 +1,4 @@ +#!/usr/bin/env ruby + +COMPONENT_ROOT = File.expand_path("..", __dir__) +require File.expand_path("../tools/test", COMPONENT_ROOT) diff --git a/railties/lib/rails.rb b/railties/lib/rails.rb index ee48043a50..00add5829d 100644 --- a/railties/lib/rails.rb +++ b/railties/lib/rails.rb @@ -7,6 +7,7 @@ require "active_support/dependencies/autoload" require "active_support/core_ext/kernel/reporting" require "active_support/core_ext/module/delegation" require "active_support/core_ext/array/extract_options" +require "active_support/core_ext/object/blank" require "rails/application" require "rails/version" @@ -67,7 +68,7 @@ module Rails # Rails.env.development? # => true # Rails.env.production? # => false def env - @_env ||= ActiveSupport::StringInquirer.new(ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development") + @_env ||= ActiveSupport::StringInquirer.new(ENV["RAILS_ENV"].presence || ENV["RACK_ENV"].presence || "development") end # Sets the Rails environment. diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb index 810750ed35..a9cc244869 100644 --- a/railties/lib/rails/application/configuration.rb +++ b/railties/lib/rails/application/configuration.rb @@ -4,7 +4,6 @@ require "rails/engine/configuration" require "rails/source_annotation_extractor" require "active_support/deprecation" -require "active_support/core_ext/string/strip" # for strip_heredoc module Rails class Application @@ -19,11 +18,11 @@ module Rails :beginning_of_week, :filter_redirect, :x, :enable_dependency_loading attr_writer :log_level - attr_reader :encoding, :api_only, :static_cache_control + attr_reader :encoding, :api_only def initialize(*) super - self.encoding = "utf-8" + self.encoding = Encoding::UTF_8 @allow_concurrency = nil @consider_all_requests_local = false @filter_parameters = [] @@ -56,35 +55,6 @@ module Rails @enable_dependency_loading = false end - def static_cache_control=(value) - ActiveSupport::Deprecation.warn <<-eow.strip_heredoc - `config.static_cache_control` is deprecated and will be removed in Rails 5.1. - Please use - `config.public_file_server.headers = { 'Cache-Control' => '#{value}' }` - instead. - eow - - @static_cache_control = value - end - - def serve_static_files - ActiveSupport::Deprecation.warn <<-eow.strip_heredoc - `config.serve_static_files` is deprecated and will be removed in Rails 5.1. - Please use `config.public_file_server.enabled` instead. - eow - - @public_file_server.enabled - end - - def serve_static_files=(value) - ActiveSupport::Deprecation.warn <<-eow.strip_heredoc - `config.serve_static_files` is deprecated and will be removed in Rails 5.1. - Please use `config.public_file_server.enabled = #{value}` instead. - eow - - @public_file_server.enabled = value - end - def encoding=(value) @encoding = value silence_warnings do @@ -161,7 +131,7 @@ module Rails def colorize_logging=(val) ActiveSupport::LogSubscriber.colorize_logging = val - self.generators.colorize_logging = val + generators.colorize_logging = val end def session_store(new_session_store = nil, **options) diff --git a/railties/lib/rails/application/default_middleware_stack.rb b/railties/lib/rails/application/default_middleware_stack.rb index 14c0a8cbe4..8fe48feefb 100644 --- a/railties/lib/rails/application/default_middleware_stack.rb +++ b/railties/lib/rails/application/default_middleware_stack.rb @@ -19,7 +19,6 @@ module Rails if config.public_file_server.enabled headers = config.public_file_server.headers || {} - headers["Cache-Control".freeze] = config.static_cache_control if config.static_cache_control middleware.use ::ActionDispatch::Static, paths["public"].first, index: config.public_file_server.index_name, headers: headers end @@ -41,12 +40,11 @@ module Rails middleware.use ::Rack::Runtime middleware.use ::Rack::MethodOverride unless config.api_only middleware.use ::ActionDispatch::RequestId + middleware.use ::ActionDispatch::RemoteIp, config.action_dispatch.ip_spoofing_check, config.action_dispatch.trusted_proxies - # Must come after Rack::MethodOverride to properly log overridden methods middleware.use ::Rails::Rack::Logger, config.log_tags middleware.use ::ActionDispatch::ShowExceptions, show_exceptions_app middleware.use ::ActionDispatch::DebugExceptions, app, config.debug_exception_response_format - middleware.use ::ActionDispatch::RemoteIp, config.action_dispatch.ip_spoofing_check, config.action_dispatch.trusted_proxies unless config.cache_classes middleware.use ::ActionDispatch::Reloader, app.reloader diff --git a/railties/lib/rails/application_controller.rb b/railties/lib/rails/application_controller.rb index f8394b8e0a..a98e51fd28 100644 --- a/railties/lib/rails/application_controller.rb +++ b/railties/lib/rails/application_controller.rb @@ -2,7 +2,7 @@ class Rails::ApplicationController < ActionController::Base # :nodoc: self.view_paths = File.expand_path("../templates", __FILE__) layout "application" - protected + private def require_local! unless local_request? diff --git a/railties/lib/rails/command.rb b/railties/lib/rails/command.rb index e8497579ae..9012ceb274 100644 --- a/railties/lib/rails/command.rb +++ b/railties/lib/rails/command.rb @@ -82,16 +82,16 @@ module Rails [[ "rails", rails ]] + groups.sort.to_a end - protected - def command_type + private + def command_type # :doc: @command_type ||= "command" end - def lookup_paths + def lookup_paths # :doc: @lookup_paths ||= %w( rails/commands commands ) end - def file_lookup_paths + def file_lookup_paths # :doc: @file_lookup_paths ||= [ "{#{lookup_paths.join(',')}}", "**", "*_command.rb" ] end end diff --git a/railties/lib/rails/command/base.rb b/railties/lib/rails/command/base.rb index 1efcd69e63..7ae190433a 100644 --- a/railties/lib/rails/command/base.rb +++ b/railties/lib/rails/command/base.rb @@ -22,7 +22,7 @@ module Rails # Tries to get the description from a USAGE file one folder above the command # root. - def desc(usage = nil, description = nil) + def desc(usage = nil, description = nil, options = {}) if usage super else @@ -130,6 +130,14 @@ module Rails end end end + + def help + if command_name = self.class.command_name + self.class.command_help(shell, command_name) + else + super + end + end end end end diff --git a/railties/lib/rails/command/behavior.rb b/railties/lib/rails/command/behavior.rb index 2e8517070c..4a92f72f16 100644 --- a/railties/lib/rails/command/behavior.rb +++ b/railties/lib/rails/command/behavior.rb @@ -16,13 +16,13 @@ module Rails @subclasses ||= [] end - protected + private # This code is based directly on the Text gem implementation. # Copyright (c) 2006-2013 Paul Battley, Michael Neumann, Tim Fletcher. # # Returns a value representing the "cost" of transforming str1 into str2. - def levenshtein_distance(str1, str2) + def levenshtein_distance(str1, str2) # :doc: s = str1 t = str2 n = s.length @@ -58,7 +58,7 @@ module Rails end # Prints a list of generators. - def print_list(base, namespaces) #:nodoc: + def print_list(base, namespaces) return if namespaces.empty? puts "#{base.camelize}:" @@ -71,7 +71,7 @@ module Rails # Receives namespaces in an array and tries to find matching generators # in the load path. - def lookup(namespaces) #:nodoc: + def lookup(namespaces) paths = namespaces_to_paths(namespaces) paths.each do |raw_path| @@ -91,7 +91,7 @@ module Rails end # This will try to load any command in the load path to show in help. - def lookup! #:nodoc: + def lookup! $LOAD_PATH.each do |base| Dir[File.join(base, *file_lookup_paths)].each do |path| begin @@ -107,7 +107,7 @@ module Rails # Convert namespaces to paths by replacing ":" for "/" and adding # an extra lookup. For example, "rails:model" should be searched # in both: "rails/model/model_generator" and "rails/model_generator". - def namespaces_to_paths(namespaces) #:nodoc: + def namespaces_to_paths(namespaces) paths = [] namespaces.each do |namespace| pieces = namespace.split(":") diff --git a/railties/lib/rails/commands/dbconsole/dbconsole_command.rb b/railties/lib/rails/commands/dbconsole/dbconsole_command.rb index 35e8673215..588fb06b15 100644 --- a/railties/lib/rails/commands/dbconsole/dbconsole_command.rb +++ b/railties/lib/rails/commands/dbconsole/dbconsole_command.rb @@ -99,14 +99,14 @@ module Rails Rails.respond_to?(:env) ? Rails.env : Rails::Command.environment end - protected - def configurations + private + def configurations # :doc: require APP_PATH ActiveRecord::Base.configurations = Rails.application.config.database_configuration ActiveRecord::Base.configurations end - def find_cmd_and_exec(commands, *args) + def find_cmd_and_exec(commands, *args) # :doc: commands = Array(commands) dirs_on_path = ENV["PATH"].to_s.split(File::PATH_SEPARATOR) diff --git a/railties/lib/rails/commands/generate/generate_command.rb b/railties/lib/rails/commands/generate/generate_command.rb index 59b2febc43..aa8dab71b0 100644 --- a/railties/lib/rails/commands/generate/generate_command.rb +++ b/railties/lib/rails/commands/generate/generate_command.rb @@ -14,6 +14,8 @@ module Rails require_application_and_environment! load_generators + ARGV.shift + Rails::Generators.invoke generator, args, behavior: :invoke, destination_root: Rails::Command.root end end diff --git a/railties/lib/rails/commands/rake/rake_command.rb b/railties/lib/rails/commands/rake/rake_command.rb index f03dc81117..075b1fd23d 100644 --- a/railties/lib/rails/commands/rake/rake_command.rb +++ b/railties/lib/rails/commands/rake/rake_command.rb @@ -28,9 +28,7 @@ module Rails return @rake_tasks if defined?(@rake_tasks) - ActiveSupport::Deprecation.silence do - require_application_and_environment! - end + require_application_and_environment! Rake::TaskManager.record_task_metadata = true Rake.application.instance_variable_set(:@name, "rails") diff --git a/railties/lib/rails/commands/server/server_command.rb b/railties/lib/rails/commands/server/server_command.rb index e9538b804c..15c636103b 100644 --- a/railties/lib/rails/commands/server/server_command.rb +++ b/railties/lib/rails/commands/server/server_command.rb @@ -7,51 +7,14 @@ require "rails/dev_caching" module Rails class Server < ::Rack::Server class Options - DEFAULT_PID_PATH = File.expand_path("tmp/pids/server.pid").freeze - def parse!(args) - args, options = args.dup, {} - - option_parser(options).parse! args - - options[:log_stdout] = options[:daemonize].blank? && (options[:environment] || Rails.env) == "development" - options[:server] = args.shift - options - end - - def option_parser(options) # :nodoc: - OptionParser.new do |opts| - opts.banner = "Usage: rails server [puma, thin etc] [options]" - - opts.separator "" - opts.separator "Options:" - - opts.on("-p", "--port=port", Integer, - "Runs Rails on the specified port.", "Default: 3000") { |v| options[:Port] = v } - opts.on("-b", "--binding=IP", String, - "Binds Rails to the specified IP.", "Default: localhost") { |v| options[:Host] = v } - opts.on("-c", "--config=file", String, - "Uses a custom rackup configuration.") { |v| options[:config] = v } - opts.on("-d", "--daemon", "Runs server as a Daemon.") { options[:daemonize] = true } - opts.on("-e", "--environment=name", String, - "Specifies the environment to run this server under (test/development/production).", - "Default: development") { |v| options[:environment] = v } - opts.on("-P", "--pid=pid", String, - "Specifies the PID file.", - "Default: tmp/pids/server.pid") { |v| options[:pid] = v } - opts.on("-C", "--[no-]dev-caching", - "Specifies whether to perform caching in development.", - "true or false") { |v| options[:caching] = v } - - opts.separator "" - - opts.on("-h", "--help", "Shows this help message.") { puts opts; exit } - end + Rails::Command::ServerCommand.new([], args).server_options end end - def initialize(*) - super + def initialize(options = nil) + @default_options = options || {} + super(@default_options) set_environment end @@ -90,15 +53,7 @@ module Rails end def default_options - super.merge( - Port: ENV.fetch("PORT", 3000).to_i, - Host: ENV.fetch("HOST", "localhost").dup, - DoNotReverseLookup: true, - environment: (ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development").dup, - daemonize: false, - caching: nil, - pid: Options::DEFAULT_PID_PATH, - restart_cmd: restart_command) + super.merge(@default_options) end private @@ -140,14 +95,33 @@ module Rails module Command class ServerCommand < Base # :nodoc: - def help - puts Rails::Server::Options.new.option_parser(Hash.new) + DEFAULT_PID_PATH = File.expand_path("tmp/pids/server.pid").freeze + + class_option :port, aliases: "-p", type: :numeric, + desc: "Runs Rails on the specified port.", banner: :port, default: 3000 + class_option :binding, aliases: "-b", type: :string, default: "localhost", + desc: "Binds Rails to the specified IP.", banner: :IP + class_option :config, aliases: "-c", type: :string, default: "config.ru", + desc: "Uses a custom rackup configuration.", banner: :file + class_option :daemon, aliases: "-d", type: :boolean, default: false, + desc: "Runs server as a Daemon." + class_option :environment, aliases: "-e", type: :string, + desc: "Specifies the environment to run this server under (development/test/production).", banner: :name + class_option :pid, aliases: "-P", type: :string, default: DEFAULT_PID_PATH, + desc: "Specifies the PID file." + class_option "dev-caching", aliases: "-C", type: :boolean, default: nil, + desc: "Specifies whether to perform caching in development." + + def initialize(args = [], local_options = {}, config = {}) + @original_options = local_options + super + @server = self.args.shift + @log_stdout = options[:daemon].blank? && (options[:environment] || Rails.env) == "development" end def perform set_application_directory! - - Rails::Server.new.tap do |server| + Rails::Server.new(server_options).tap do |server| # Require application after server sets environment to propagate # the --environment option. require APP_PATH @@ -155,6 +129,45 @@ module Rails server.start end end + + no_commands do + def server_options + { + server: @server, + log_stdout: @log_stdout, + Port: port, + Host: host, + DoNotReverseLookup: true, + config: options[:config], + environment: environment, + daemonize: options[:daemon], + pid: options[:pid], + caching: options["dev-caching"], + restart_cmd: restart_command + } + end + end + + private + def port + ENV.fetch("PORT", options[:port]).to_i + end + + def host + ENV.fetch("HOST", options[:binding]) + end + + def environment + options[:environment] || Rails::Command.environment + end + + def restart_command + "bin/rails server #{@server} #{@original_options.join(" ")}" + end + + def self.banner(*) + "rails server [puma, thin etc] [options]" + end end end end diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb index 53334489aa..13af6051ce 100644 --- a/railties/lib/rails/engine.rb +++ b/railties/lib/rails/engine.rb @@ -380,7 +380,7 @@ module Rails def isolate_namespace(mod) engine_name(generate_railtie_name(mod.name)) - self.routes.default_scope = { module: ActiveSupport::Inflector.underscore(mod.name) } + routes.default_scope = { module: ActiveSupport::Inflector.underscore(mod.name) } self.isolated = true unless mod.respond_to?(:railtie_namespace) @@ -573,7 +573,7 @@ module Rails end initializer :add_routing_paths do |app| - routing_paths = self.paths["config/routes.rb"].existent + routing_paths = paths["config/routes.rb"].existent if routes? || routing_paths.any? app.routes_reloader.paths.unshift(*routing_paths) @@ -643,18 +643,20 @@ module Rails protected - def load_config_initializer(initializer) - ActiveSupport::Notifications.instrument("load_config_initializer.railties", initializer: initializer) do - load(initializer) - end - end - def run_tasks_blocks(*) #:nodoc: super paths["lib/tasks"].existent.sort.each { |ext| load(ext) } end - def has_migrations? #:nodoc: + private + + def load_config_initializer(initializer) # :doc: + ActiveSupport::Notifications.instrument("load_config_initializer.railties", initializer: initializer) do + load(initializer) + end + end + + def has_migrations? paths["db/migrate"].existent.any? end @@ -671,24 +673,22 @@ module Rails Pathname.new File.realpath root end - def default_middleware_stack #:nodoc: + def default_middleware_stack ActionDispatch::MiddlewareStack.new end - def _all_autoload_once_paths #:nodoc: + def _all_autoload_once_paths config.autoload_once_paths end - def _all_autoload_paths #:nodoc: + def _all_autoload_paths @_all_autoload_paths ||= (config.autoload_paths + config.eager_load_paths + config.autoload_once_paths).uniq end - def _all_load_paths #:nodoc: + def _all_load_paths @_all_load_paths ||= (config.paths.load_paths + _all_autoload_paths).uniq end - private - def build_request(env) env.merge!(env_config) req = ActionDispatch::Request.new env diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb index c5b7caaf21..99bda728ee 100644 --- a/railties/lib/rails/generators.rb +++ b/railties/lib/rails/generators.rb @@ -67,246 +67,247 @@ module Rails } } - def self.configure!(config) #:nodoc: - api_only! if config.api_only - no_color! unless config.colorize_logging - aliases.deep_merge! config.aliases - options.deep_merge! config.options - fallbacks.merge! config.fallbacks - templates_path.concat config.templates - templates_path.uniq! - hide_namespaces(*config.hidden_namespaces) - end + class << self + def configure!(config) #:nodoc: + api_only! if config.api_only + no_color! unless config.colorize_logging + aliases.deep_merge! config.aliases + options.deep_merge! config.options + fallbacks.merge! config.fallbacks + templates_path.concat config.templates + templates_path.uniq! + hide_namespaces(*config.hidden_namespaces) + end - def self.templates_path #:nodoc: - @templates_path ||= [] - end + def templates_path #:nodoc: + @templates_path ||= [] + end - def self.aliases #:nodoc: - @aliases ||= DEFAULT_ALIASES.dup - end + def aliases #:nodoc: + @aliases ||= DEFAULT_ALIASES.dup + end - def self.options #:nodoc: - @options ||= DEFAULT_OPTIONS.dup - end + def options #:nodoc: + @options ||= DEFAULT_OPTIONS.dup + end - # Hold configured generators fallbacks. If a plugin developer wants a - # generator group to fallback to another group in case of missing generators, - # they can add a fallback. - # - # For example, shoulda is considered a test_framework and is an extension - # of test_unit. However, most part of shoulda generators are similar to - # test_unit ones. - # - # Shoulda then can tell generators to search for test_unit generators when - # some of them are not available by adding a fallback: - # - # Rails::Generators.fallbacks[:shoulda] = :test_unit - def self.fallbacks - @fallbacks ||= {} - end + # Hold configured generators fallbacks. If a plugin developer wants a + # generator group to fallback to another group in case of missing generators, + # they can add a fallback. + # + # For example, shoulda is considered a test_framework and is an extension + # of test_unit. However, most part of shoulda generators are similar to + # test_unit ones. + # + # Shoulda then can tell generators to search for test_unit generators when + # some of them are not available by adding a fallback: + # + # Rails::Generators.fallbacks[:shoulda] = :test_unit + def fallbacks + @fallbacks ||= {} + end - # Configure generators for API only applications. It basically hides - # everything that is usually browser related, such as assets and session - # migration generators, and completely disable helpers and assets - # so generators such as scaffold won't create them. - def self.api_only! - hide_namespaces "assets", "helper", "css", "js" - - options[:rails].merge!( - api: true, - assets: false, - helper: false, - template_engine: nil - ) - - if ARGV.first == "mailer" - options[:rails].merge!(template_engine: :erb) + # Configure generators for API only applications. It basically hides + # everything that is usually browser related, such as assets and session + # migration generators, and completely disable helpers and assets + # so generators such as scaffold won't create them. + def api_only! + hide_namespaces "assets", "helper", "css", "js" + + options[:rails].merge!( + api: true, + assets: false, + helper: false, + template_engine: nil + ) + + if ARGV.first == "mailer" + options[:rails].merge!(template_engine: :erb) + end end - end - # Remove the color from output. - def self.no_color! - Thor::Base.shell = Thor::Shell::Basic - end + # Remove the color from output. + def no_color! + Thor::Base.shell = Thor::Shell::Basic + end - # Returns an array of generator namespaces that are hidden. - # Generator namespaces may be hidden for a variety of reasons. - # Some are aliased such as "rails:migration" and can be - # invoked with the shorter "migration", others are private to other generators - # such as "css:scaffold". - def self.hidden_namespaces - @hidden_namespaces ||= begin - orm = options[:rails][:orm] - test = options[:rails][:test_framework] - template = options[:rails][:template_engine] - css = options[:rails][:stylesheet_engine] - - [ - "rails", - "resource_route", - "#{orm}:migration", - "#{orm}:model", - "#{test}:controller", - "#{test}:helper", - "#{test}:integration", - "#{test}:mailer", - "#{test}:model", - "#{test}:scaffold", - "#{test}:view", - "#{test}:job", - "#{template}:controller", - "#{template}:scaffold", - "#{template}:mailer", - "#{css}:scaffold", - "#{css}:assets", - "css:assets", - "css:scaffold" - ] + # Returns an array of generator namespaces that are hidden. + # Generator namespaces may be hidden for a variety of reasons. + # Some are aliased such as "rails:migration" and can be + # invoked with the shorter "migration", others are private to other generators + # such as "css:scaffold". + def hidden_namespaces + @hidden_namespaces ||= begin + orm = options[:rails][:orm] + test = options[:rails][:test_framework] + template = options[:rails][:template_engine] + css = options[:rails][:stylesheet_engine] + + [ + "rails", + "resource_route", + "#{orm}:migration", + "#{orm}:model", + "#{test}:controller", + "#{test}:helper", + "#{test}:integration", + "#{test}:mailer", + "#{test}:model", + "#{test}:scaffold", + "#{test}:view", + "#{test}:job", + "#{template}:controller", + "#{template}:scaffold", + "#{template}:mailer", + "#{css}:scaffold", + "#{css}:assets", + "css:assets", + "css:scaffold" + ] + end end - end - class << self def hide_namespaces(*namespaces) hidden_namespaces.concat(namespaces) end alias hide_namespace hide_namespaces - end - # Show help message with available generators. - def self.help(command = "generate") - puts "Usage: rails #{command} GENERATOR [args] [options]" - puts - puts "General options:" - puts " -h, [--help] # Print generator's options and usage" - puts " -p, [--pretend] # Run but do not make any changes" - puts " -f, [--force] # Overwrite files that already exist" - puts " -s, [--skip] # Skip files that already exist" - puts " -q, [--quiet] # Suppress status output" - puts - puts "Please choose a generator below." - puts - - print_generators - end - - def self.public_namespaces - lookup! - subclasses.map(&:namespace) - end - - def self.print_generators - sorted_groups.each { |b, n| print_list(b, n) } - end + # Show help message with available generators. + def help(command = "generate") + puts "Usage: rails #{command} GENERATOR [args] [options]" + puts + puts "General options:" + puts " -h, [--help] # Print generator's options and usage" + puts " -p, [--pretend] # Run but do not make any changes" + puts " -f, [--force] # Overwrite files that already exist" + puts " -s, [--skip] # Skip files that already exist" + puts " -q, [--quiet] # Suppress status output" + puts + puts "Please choose a generator below." + puts + + print_generators + end - def self.sorted_groups - namespaces = public_namespaces - namespaces.sort! + def public_namespaces + lookup! + subclasses.map(&:namespace) + end - groups = Hash.new { |h, k| h[k] = [] } - namespaces.each do |namespace| - base = namespace.split(":").first - groups[base] << namespace + def print_generators + sorted_groups.each { |b, n| print_list(b, n) } end - rails = groups.delete("rails") - rails.map! { |n| n.sub(/^rails:/, "") } - rails.delete("app") - rails.delete("plugin") + def sorted_groups + namespaces = public_namespaces + namespaces.sort! - hidden_namespaces.each { |n| groups.delete(n.to_s) } + groups = Hash.new { |h, k| h[k] = [] } + namespaces.each do |namespace| + base = namespace.split(":").first + groups[base] << namespace + end - [[ "rails", rails ]] + groups.sort.to_a - end + rails = groups.delete("rails") + rails.map! { |n| n.sub(/^rails:/, "") } + rails.delete("app") + rails.delete("plugin") - # Rails finds namespaces similar to Thor, it only adds one rule: - # - # Generators names must end with "_generator.rb". This is required because Rails - # looks in load paths and loads the generator just before it's going to be used. - # - # find_by_namespace :webrat, :rails, :integration - # - # Will search for the following generators: - # - # "rails:webrat", "webrat:integration", "webrat" - # - # Notice that "rails:generators:webrat" could be loaded as well, what - # Rails looks for is the first and last parts of the namespace. - def self.find_by_namespace(name, base = nil, context = nil) #:nodoc: - lookups = [] - lookups << "#{base}:#{name}" if base - lookups << "#{name}:#{context}" if context - - unless base || context - unless name.to_s.include?(?:) - lookups << "#{name}:#{name}" - lookups << "rails:#{name}" - end - lookups << "#{name}" + hidden_namespaces.each { |n| groups.delete(n.to_s) } + + [[ "rails", rails ]] + groups.sort.to_a end - lookup(lookups) + # Rails finds namespaces similar to Thor, it only adds one rule: + # + # Generators names must end with "_generator.rb". This is required because Rails + # looks in load paths and loads the generator just before it's going to be used. + # + # find_by_namespace :webrat, :rails, :integration + # + # Will search for the following generators: + # + # "rails:webrat", "webrat:integration", "webrat" + # + # Notice that "rails:generators:webrat" could be loaded as well, what + # Rails looks for is the first and last parts of the namespace. + def find_by_namespace(name, base = nil, context = nil) #:nodoc: + lookups = [] + lookups << "#{base}:#{name}" if base + lookups << "#{name}:#{context}" if context + + unless base || context + unless name.to_s.include?(?:) + lookups << "#{name}:#{name}" + lookups << "rails:#{name}" + end + lookups << "#{name}" + end - namespaces = Hash[subclasses.map { |klass| [klass.namespace, klass] }] - lookups.each do |namespace| + lookup(lookups) - klass = namespaces[namespace] - return klass if klass - end + namespaces = Hash[subclasses.map { |klass| [klass.namespace, klass] }] + lookups.each do |namespace| - invoke_fallbacks_for(name, base) || invoke_fallbacks_for(context, name) - end + klass = namespaces[namespace] + return klass if klass + end - # Receives a namespace, arguments and the behavior to invoke the generator. - # It's used as the default entry point for generate, destroy and update - # commands. - def self.invoke(namespace, args = ARGV, config = {}) - names = namespace.to_s.split(":") - if klass = find_by_namespace(names.pop, names.any? && names.join(":")) - args << "--help" if args.empty? && klass.arguments.any?(&:required?) - klass.start(args, config) - else - options = sorted_groups.flat_map(&:last) - suggestions = options.sort_by { |suggested| levenshtein_distance(namespace.to_s, suggested) }.first(3) - msg = "Could not find generator '#{namespace}'. " - msg << "Maybe you meant #{ suggestions.map { |s| "'#{s}'" }.to_sentence(last_word_connector: " or ", locale: :en) }\n" - msg << "Run `rails generate --help` for more options." - puts msg + invoke_fallbacks_for(name, base) || invoke_fallbacks_for(context, name) end - end - protected - def self.print_list(base, namespaces) - namespaces = namespaces.reject { |n| hidden_namespaces.include?(n) } - super + # Receives a namespace, arguments and the behavior to invoke the generator. + # It's used as the default entry point for generate, destroy and update + # commands. + def invoke(namespace, args = ARGV, config = {}) + names = namespace.to_s.split(":") + if klass = find_by_namespace(names.pop, names.any? && names.join(":")) + args << "--help" if args.empty? && klass.arguments.any?(&:required?) + klass.start(args, config) + else + options = sorted_groups.flat_map(&:last) + suggestions = options.sort_by { |suggested| levenshtein_distance(namespace.to_s, suggested) }.first(3) + msg = "Could not find generator '#{namespace}'. " + msg << "Maybe you meant #{ suggestions.map { |s| "'#{s}'" }.to_sentence(last_word_connector: " or ", locale: :en) }\n" + msg << "Run `rails generate --help` for more options." + puts msg + end end - # Try fallbacks for the given base. - def self.invoke_fallbacks_for(name, base) #:nodoc: - return nil unless base && fallbacks[base.to_sym] - invoked_fallbacks = [] - - Array(fallbacks[base.to_sym]).each do |fallback| - next if invoked_fallbacks.include?(fallback) - invoked_fallbacks << fallback + private - klass = find_by_namespace(name, fallback) - return klass if klass + def print_list(base, namespaces) # :doc: + namespaces = namespaces.reject { |n| hidden_namespaces.include?(n) } + super end - nil - end + # Try fallbacks for the given base. + def invoke_fallbacks_for(name, base) + return nil unless base && fallbacks[base.to_sym] + invoked_fallbacks = [] - def self.command_type - @command_type ||= "generator" - end + Array(fallbacks[base.to_sym]).each do |fallback| + next if invoked_fallbacks.include?(fallback) + invoked_fallbacks << fallback - def self.lookup_paths - @lookup_paths ||= %w( rails/generators generators ) - end + klass = find_by_namespace(name, fallback) + return klass if klass + end - def self.file_lookup_paths - @file_lookup_paths ||= [ "{#{lookup_paths.join(',')}}", "**", "*_generator.rb" ] - end + nil + end + + def command_type # :doc: + @command_type ||= "generator" + end + + def lookup_paths # :doc: + @lookup_paths ||= %w( rails/generators generators ) + end + + def file_lookup_paths # :doc: + @file_lookup_paths ||= [ "{#{lookup_paths.join(',')}}", "**", "*_generator.rb" ] + end + end end end diff --git a/railties/lib/rails/generators/actions.rb b/railties/lib/rails/generators/actions.rb index 8a60560f86..0bd0615b7e 100644 --- a/railties/lib/rails/generators/actions.rb +++ b/railties/lib/rails/generators/actions.rb @@ -260,23 +260,23 @@ module Rails @after_bundle_callbacks << block end - protected + private # Define log for backwards compatibility. If just one argument is sent, # invoke say, otherwise invoke say_status. Differently from say and # similarly to say_status, this method respects the quiet? option given. - def log(*args) + def log(*args) # :doc: if args.size == 1 say args.first.to_s unless options.quiet? else - args << (self.behavior == :invoke ? :green : :red) + args << (behavior == :invoke ? :green : :red) say_status(*args) end end # Runs the supplied command using either "rake ..." or "rails ..." # based on the executor parameter provided. - def execute_command(executor, command, options = {}) + def execute_command(executor, command, options = {}) # :doc: log executor, command env = options[:env] || ENV["RAILS_ENV"] || "development" sudo = options[:sudo] && !Gem.win_platform? ? "sudo " : "" @@ -284,7 +284,7 @@ module Rails end # Add an extension to the given name based on the platform. - def extify(name) + def extify(name) # :doc: if Gem.win_platform? "#{name}.bat" else @@ -294,7 +294,7 @@ module Rails # Surround string with single quotes if there is no quotes. # Otherwise fall back to double quotes - def quote(value) + def quote(value) # :doc: return value.inspect unless value.is_a? String if value.include?("'") diff --git a/railties/lib/rails/generators/actions/create_migration.rb b/railties/lib/rails/generators/actions/create_migration.rb index 587c61fd42..f677e545e5 100644 --- a/railties/lib/rails/generators/actions/create_migration.rb +++ b/railties/lib/rails/generators/actions/create_migration.rb @@ -37,9 +37,9 @@ module Rails end alias :exists? :existing_migration - protected + private - def on_conflict_behavior + def on_conflict_behavior # :doc: options = base.options.merge(config) if identical? say_status :identical, :blue, relative_existing_migration @@ -60,7 +60,7 @@ module Rails end end - def say_status(status, color, message = relative_destination) + def say_status(status, color, message = relative_destination) # :doc: base.shell.say_status(status, message, color) if config[:verbose] end end diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb index 15cc070e35..ea88afe9f4 100644 --- a/railties/lib/rails/generators/app_base.rb +++ b/railties/lib/rails/generators/app_base.rb @@ -42,9 +42,6 @@ module Rails class_option :skip_gemfile, type: :boolean, default: false, desc: "Don't create a Gemfile" - class_option :skip_bundle, type: :boolean, aliases: "-B", default: false, - desc: "Don't run bundle install" - class_option :skip_git, type: :boolean, aliases: "-G", default: false, desc: "Skip .gitignore file" @@ -108,9 +105,9 @@ module Rails convert_database_option_for_jruby end - protected + private - def gemfile_entry(name, *args) + def gemfile_entry(name, *args) # :doc: options = args.extract_options! version = args.first github = options[:github] @@ -126,7 +123,7 @@ module Rails self end - def gemfile_entries + def gemfile_entries # :doc: [rails_gemfile_entry, database_gemfile_entry, webserver_gemfile_entry, @@ -139,13 +136,13 @@ module Rails @extra_entries].flatten.find_all(&@gem_filter) end - def add_gem_entry_filter + def add_gem_entry_filter # :doc: @gem_filter = lambda { |next_filter, entry| yield(entry) && next_filter.call(entry) }.curry[@gem_filter] end - def builder + def builder # :doc: @builder ||= begin builder_class = get_builder_class builder_class.include(ActionMethods) @@ -153,24 +150,24 @@ module Rails end end - def build(meth, *args) + def build(meth, *args) # :doc: builder.send(meth, *args) if builder.respond_to?(meth) end - def create_root + def create_root # :doc: valid_const? empty_directory "." FileUtils.cd(destination_root) unless options[:pretend] end - def apply_rails_template + def apply_rails_template # :doc: apply rails_template if rails_template rescue Thor::Error, LoadError, Errno::ENOENT => e raise Error, "The template [#{rails_template}] could not be loaded. Error: #{e}" end - def set_default_accessors! + def set_default_accessors! # :doc: self.destination_root = File.expand_path(app_path, destination_root) self.rails_template = \ case options[:template] @@ -183,32 +180,32 @@ module Rails end end - def database_gemfile_entry + def database_gemfile_entry # :doc: return [] if options[:skip_active_record] gem_name, gem_version = gem_for_database GemfileEntry.version gem_name, gem_version, "Use #{options[:database]} as the database for Active Record" end - def webserver_gemfile_entry + def webserver_gemfile_entry # :doc: return [] if options[:skip_puma] comment = "Use Puma as the app server" GemfileEntry.new("puma", "~> 3.0", comment) end - def include_all_railties? + def include_all_railties? # :doc: options.values_at(:skip_active_record, :skip_action_mailer, :skip_test, :skip_sprockets, :skip_action_cable).none? end - def comment_if(value) + def comment_if(value) # :doc: options[value] ? "# " : "" end - def keeps? + def keeps? # :doc: !options[:skip_keeps] end - def sqlite3? + def sqlite3? # :doc: !options[:skip_active_record] && options[:database] == "sqlite3" end @@ -246,6 +243,7 @@ module Rails def rails_gemfile_entry dev_edge_common = [ + GemfileEntry.github("arel", "rails/arel") ] if options.dev? [ diff --git a/railties/lib/rails/generators/base.rb b/railties/lib/rails/generators/base.rb index c707bbfcbf..ba031d7b5d 100644 --- a/railties/lib/rails/generators/base.rb +++ b/railties/lib/rails/generators/base.rb @@ -239,11 +239,11 @@ module Rails end end - protected + private # Check whether the given class names are already taken by user # application or Ruby on Rails. - def class_collisions(*class_names) #:nodoc: + def class_collisions(*class_names) return unless behavior == :invoke class_names.flatten.each do |class_name| @@ -264,7 +264,7 @@ module Rails end # Takes in an array of nested modules and extracts the last module - def extract_last_module(nesting) + def extract_last_module(nesting) # :doc: nesting.inject(Object) do |last_module, nest| break unless last_module.const_defined?(nest, false) last_module.const_get(nest) @@ -272,12 +272,12 @@ module Rails end # Use Rails default banner. - def self.banner + def self.banner # :doc: "rails generate #{namespace.sub(/^rails:/, '')} #{arguments.map(&:usage).join(' ')} [options]".gsub(/\s+/, " ") end # Sets the base_name taking into account the current class namespace. - def self.base_name + def self.base_name # :doc: @base_name ||= begin if base = name.to_s.split("::").first base.underscore @@ -287,7 +287,7 @@ module Rails # Removes the namespaces and get the generator name. For example, # Rails::Generators::ModelGenerator will return "model" as generator name. - def self.generator_name + def self.generator_name # :doc: @generator_name ||= begin if generator = name.to_s.split("::").last generator.sub!(/Generator$/, "") @@ -298,18 +298,18 @@ module Rails # Returns the default value for the option name given doing a lookup in # Rails::Generators.options. - def self.default_value_for_option(name, options) + def self.default_value_for_option(name, options) # :doc: default_for_option(Rails::Generators.options, name, options, options[:default]) end # Returns default aliases for the option name given doing a lookup in # Rails::Generators.aliases. - def self.default_aliases_for_option(name, options) + def self.default_aliases_for_option(name, options) # :doc: default_for_option(Rails::Generators.aliases, name, options, options[:aliases]) end # Returns default for the option name given doing a lookup in config. - def self.default_for_option(config, name, options, default) + def self.default_for_option(config, name, options, default) # :doc: if generator_name && (c = config[generator_name.to_sym]) && c.key?(name) c[name] elsif base_name && (c = config[base_name.to_sym]) && c.key?(name) @@ -331,7 +331,7 @@ module Rails def self.prepare_for_invocation(name, value) #:nodoc: return super unless value.is_a?(String) || value.is_a?(Symbol) - if value && constants = self.hooks[name] + if value && constants = hooks[name] value = name if TrueClass === value Rails::Generators.find_by_namespace(value, *constants) elsif klass = Rails::Generators.find_by_namespace(value) @@ -343,7 +343,7 @@ module Rails # Small macro to add ruby as an option to the generator with proper # default value plus an instance helper method called shebang. - def self.add_shebang_option! + def self.add_shebang_option! # :doc: class_option :ruby, type: :string, aliases: "-r", default: Thor::Util.ruby_command, desc: "Path to the Ruby binary of your choice", banner: "PATH" @@ -361,7 +361,7 @@ module Rails } end - def self.usage_path + def self.usage_path # :doc: paths = [ source_root && File.expand_path("../USAGE", source_root), default_generator_root && File.join(default_generator_root, "USAGE") @@ -369,7 +369,7 @@ module Rails paths.compact.detect { |path| File.exist? path } end - def self.default_generator_root + def self.default_generator_root # :doc: path = File.expand_path(File.join(base_name, generator_name), base_root) path if File.exist?(path) end diff --git a/railties/lib/rails/generators/erb.rb b/railties/lib/rails/generators/erb.rb index d01502002f..d5e326d6ee 100644 --- a/railties/lib/rails/generators/erb.rb +++ b/railties/lib/rails/generators/erb.rb @@ -3,7 +3,7 @@ require "rails/generators/named_base" module Erb # :nodoc: module Generators # :nodoc: class Base < Rails::Generators::NamedBase #:nodoc: - protected + private def formats [format] diff --git a/railties/lib/rails/generators/erb/mailer/mailer_generator.rb b/railties/lib/rails/generators/erb/mailer/mailer_generator.rb index f150240908..d74b5655d5 100644 --- a/railties/lib/rails/generators/erb/mailer/mailer_generator.rb +++ b/railties/lib/rails/generators/erb/mailer/mailer_generator.rb @@ -9,7 +9,7 @@ module Erb # :nodoc: view_base_path = File.join("app/views", class_path, file_name + "_mailer") empty_directory view_base_path - if self.behavior == :invoke + if behavior == :invoke formats.each do |format| layout_path = File.join("app/views/layouts", class_path, filename_with_extensions("mailer", format)) template filename_with_extensions(:layout, format), layout_path @@ -26,7 +26,7 @@ module Erb # :nodoc: end end - protected + private def formats [:text, :html] diff --git a/railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb b/railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb index 154d85f381..0d77ef21da 100644 --- a/railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb +++ b/railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb @@ -21,7 +21,7 @@ module Erb # :nodoc: end end - protected + private def available_views %w(index edit show new _form) diff --git a/railties/lib/rails/generators/migration.rb b/railties/lib/rails/generators/migration.rb index 0d63b9a5c9..82481169c3 100644 --- a/railties/lib/rails/generators/migration.rb +++ b/railties/lib/rails/generators/migration.rb @@ -35,7 +35,7 @@ module Rails end def set_migration_assigns!(destination) - destination = File.expand_path(destination, self.destination_root) + destination = File.expand_path(destination, destination_root) migration_dir = File.dirname(destination) @migration_number = self.class.next_migration_number(migration_dir) diff --git a/railties/lib/rails/generators/named_base.rb b/railties/lib/rails/generators/named_base.rb index 70f63dc672..6f1925928b 100644 --- a/railties/lib/rails/generators/named_base.rb +++ b/railties/lib/rails/generators/named_base.rb @@ -32,141 +32,145 @@ module Rails end end + # TODO Change this to private once we've dropped Ruby 2.2 support. + # Workaround for Ruby 2.2 "private attribute?" warning. protected attr_reader :file_name + private + # FIXME: We are avoiding to use alias because a bug on thor that make # this method public and add it to the task list. - def singular_name + def singular_name # :doc: file_name end # Wrap block with namespace of current application # if namespace exists and is not skipped - def module_namespacing(&block) + def module_namespacing(&block) # :doc: content = capture(&block) content = wrap_with_namespace(content) if namespaced? concat(content) end - def indent(content, multiplier = 2) + def indent(content, multiplier = 2) # :doc: spaces = " " * multiplier content.each_line.map { |line| line.blank? ? line : "#{spaces}#{line}" }.join end - def wrap_with_namespace(content) + def wrap_with_namespace(content) # :doc: content = indent(content).chomp "module #{namespace.name}\n#{content}\nend\n" end - def inside_template + def inside_template # :doc: @inside_template = true yield ensure @inside_template = false end - def inside_template? + def inside_template? # :doc: @inside_template end - def namespace + def namespace # :doc: Rails::Generators.namespace end - def namespaced? + def namespaced? # :doc: !options[:skip_namespace] && namespace end - def file_path + def file_path # :doc: @file_path ||= (class_path + [file_name]).join("/") end - def class_path + def class_path # :doc: inside_template? || !namespaced? ? regular_class_path : namespaced_class_path end - def regular_class_path + def regular_class_path # :doc: @class_path end - def namespaced_class_path + def namespaced_class_path # :doc: @namespaced_class_path ||= [namespaced_path] + @class_path end - def namespaced_path + def namespaced_path # :doc: @namespaced_path ||= namespace.name.split("::").first.underscore end - def class_name + def class_name # :doc: (class_path + [file_name]).map!(&:camelize).join("::") end - def human_name + def human_name # :doc: @human_name ||= singular_name.humanize end - def plural_name + def plural_name # :doc: @plural_name ||= singular_name.pluralize end - def i18n_scope + def i18n_scope # :doc: @i18n_scope ||= file_path.tr("/", ".") end - def table_name + def table_name # :doc: @table_name ||= begin base = pluralize_table_names? ? plural_name : singular_name (class_path + [base]).join("_") end end - def uncountable? + def uncountable? # :doc: singular_name == plural_name end - def index_helper + def index_helper # :doc: uncountable? ? "#{plural_table_name}_index" : plural_table_name end - def show_helper + def show_helper # :doc: "#{singular_table_name}_url(@#{singular_table_name})" end - def edit_helper + def edit_helper # :doc: "edit_#{show_helper}" end - def new_helper + def new_helper # :doc: "new_#{singular_table_name}_url" end - def singular_table_name + def singular_table_name # :doc: @singular_table_name ||= (pluralize_table_names? ? table_name.singularize : table_name) end - def plural_table_name + def plural_table_name # :doc: @plural_table_name ||= (pluralize_table_names? ? table_name : table_name.pluralize) end - def plural_file_name + def plural_file_name # :doc: @plural_file_name ||= file_name.pluralize end - def fixture_file_name + def fixture_file_name # :doc: @fixture_file_name ||= (pluralize_table_names? ? plural_file_name : file_name) end - def route_url + def route_url # :doc: @route_url ||= class_path.collect { |dname| "/" + dname }.join + "/" + plural_file_name end - def url_helper_prefix + def url_helper_prefix # :doc: @url_helper_prefix ||= (class_path + [file_name]).join("_") end # Tries to retrieve the application name or simply return application. - def application_name + def application_name # :doc: if defined?(Rails) && Rails.application Rails.application.class.name.split("::").first.underscore else @@ -174,20 +178,20 @@ module Rails end end - def assign_names!(name) #:nodoc: + def assign_names!(name) @class_path = name.include?("/") ? name.split("/") : name.split("::") @class_path.map!(&:underscore) @file_name = @class_path.pop end # Convert attributes array into GeneratedAttribute objects. - def parse_attributes! #:nodoc: + def parse_attributes! self.attributes = (attributes || []).map do |attr| Rails::Generators::GeneratedAttribute.parse(attr) end end - def attributes_names + def attributes_names # :doc: @attributes_names ||= attributes.each_with_object([]) do |a, names| names << a.column_name names << "password_confirmation" if a.password_digest? @@ -195,11 +199,11 @@ module Rails end end - def pluralize_table_names? + def pluralize_table_names? # :doc: !defined?(ActiveRecord::Base) || ActiveRecord::Base.pluralize_table_names end - def mountable_engine? + def mountable_engine? # :doc: defined?(ENGINE_ROOT) && namespaced? end @@ -213,9 +217,9 @@ module Rails # If the generator is invoked with class name Admin, it will check for # the presence of "AdminDecorator". # - def self.check_class_collision(options = {}) + def self.check_class_collision(options = {}) # :doc: define_method :check_class_collision do - name = if self.respond_to?(:controller_class_name) # for ScaffoldBase + name = if respond_to?(:controller_class_name) # for ScaffoldBase controller_class_name else class_name diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb index 19d3ba2f0f..8efdfdcb44 100644 --- a/railties/lib/rails/generators/rails/app/app_generator.rb +++ b/railties/lib/rails/generators/rails/app/app_generator.rb @@ -175,6 +175,9 @@ module Rails class_option :api, type: :boolean, desc: "Preconfigure smaller stack for API only apps" + class_option :skip_bundle, type: :boolean, aliases: "-B", default: false, + desc: "Don't run bundle install" + def initialize(*args) super @@ -320,7 +323,6 @@ module Rails def delete_action_mailer_files_skipping_action_mailer if options[:skip_action_mailer] - remove_file "app/mailers/application_mailer.rb" remove_file "app/views/layouts/mailer.html.erb" remove_file "app/views/layouts/mailer.text.erb" remove_dir "app/mailers" @@ -348,8 +350,8 @@ module Rails end end - def delete_bin_yarn_if_api_option - remove_file "bin/yarn" if options[:api] + def delete_bin_yarn_if_skip_yarn_option + remove_file "bin/yarn" if options[:skip_yarn] end def finish_template @@ -363,12 +365,12 @@ module Rails @after_bundle_callbacks.each(&:call) end - protected - def self.banner "rails new #{arguments.map(&:usage).join(' ')} [options]" end + private + # Define file as an alias to create_file for backwards compatibility. def file(*args, &block) create_file(*args, &block) diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml index a2b2a64ba6..8bc8735a8e 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml @@ -1,4 +1,4 @@ -# MySQL. Versions 5.0 and up are supported. +# MySQL. Versions 5.1.10 and up are supported. # # Install the MySQL driver: # gem install activerecord-jdbcmysql-adapter diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml index d987cf303b..269af1470d 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml @@ -1,4 +1,4 @@ -# MySQL. Versions 5.0 and up are supported. +# MySQL. Versions 5.1.10 and up are supported. # # Install the MySQL driver # gem install mysql2 diff --git a/railties/lib/rails/generators/rails/assets/assets_generator.rb b/railties/lib/rails/generators/rails/assets/assets_generator.rb index 265dada2ca..95d00c2d39 100644 --- a/railties/lib/rails/generators/rails/assets/assets_generator.rb +++ b/railties/lib/rails/generators/rails/assets/assets_generator.rb @@ -7,7 +7,7 @@ module Rails class_option :javascript_engine, desc: "Engine for JavaScripts" class_option :stylesheet_engine, desc: "Engine for Stylesheets" - protected + private def asset_name file_name diff --git a/railties/lib/rails/generators/rails/controller/controller_generator.rb b/railties/lib/rails/generators/rails/controller/controller_generator.rb index 01214dc919..06bdb8b5ce 100644 --- a/railties/lib/rails/generators/rails/controller/controller_generator.rb +++ b/railties/lib/rails/generators/rails/controller/controller_generator.rb @@ -16,7 +16,7 @@ module Rails unless options[:skip_routes] actions.reverse_each do |action| # route prepends two spaces onto the front of the string that is passed, this corrects that. - route generate_routing_code(action) + route indent(generate_routing_code(action), 2)[2..-1] end end end @@ -34,27 +34,30 @@ module Rails # end # end def generate_routing_code(action) - depth = regular_class_path.length + depth = 0 + lines = [] + # Create 'namespace' ladder # namespace :foo do # namespace :bar do - namespace_ladder = regular_class_path.each_with_index.map do |ns, i| - indent(" namespace :#{ns} do\n", i * 2) - end.join[2..-1] + regular_class_path.each do |ns| + lines << indent("namespace :#{ns} do\n", depth * 2) + depth += 1 + end # Create route # get 'baz/index' - route = indent(%{ get '#{file_name}/#{action}'\n}, depth * 2) + lines << indent(%{get '#{file_name}/#{action}'\n}, depth * 2) # Create `end` ladder # end # end - end_ladder = (1..depth).reverse_each.map do |i| - indent("end\n", i * 2) - end.join + until depth.zero? + depth -= 1 + lines << indent("end\n", depth * 2) + end - # Combine the 3 parts to generate complete route entry - "#{namespace_ladder}#{route}#{end_ladder}" + lines.join end end end diff --git a/railties/lib/rails/generators/rails/generator/generator_generator.rb b/railties/lib/rails/generators/rails/generator/generator_generator.rb index 8040ec5e7b..299a7da5f1 100644 --- a/railties/lib/rails/generators/rails/generator/generator_generator.rb +++ b/railties/lib/rails/generators/rails/generator/generator_generator.rb @@ -12,7 +12,7 @@ module Rails hook_for :test_framework - protected + private def generator_dir if options[:namespace] diff --git a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb index 2186fa4ded..4cf4f8cd9a 100644 --- a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb +++ b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb @@ -286,7 +286,7 @@ task default: :test @namespaced_name ||= name.tr("-", "/") end - protected + private def create_dummy_app(path = nil) dummy_path(path) if path diff --git a/railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb b/railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb index e4f3161ffd..4cc1e2d0fd 100644 --- a/railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb +++ b/railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb @@ -20,7 +20,12 @@ module Rails template template_file, File.join("app/controllers", controller_class_path, "#{controller_file_name}_controller.rb") end - hook_for :template_engine, :test_framework, as: :scaffold + + hook_for :template_engine, as: :scaffold do |template_engine| + invoke template_engine unless options.api? + end + + hook_for :test_framework, as: :scaffold # Invoke the helper using the controller name (pluralized) hook_for :helper, as: :scaffold do |invoked| diff --git a/railties/lib/rails/generators/resource_helpers.rb b/railties/lib/rails/generators/resource_helpers.rb index a28977319a..978b053308 100644 --- a/railties/lib/rails/generators/resource_helpers.rb +++ b/railties/lib/rails/generators/resource_helpers.rb @@ -23,10 +23,14 @@ module Rails assign_controller_names!(controller_name.pluralize) end + # TODO Change this to private once we've dropped Ruby 2.2 support. + # Workaround for Ruby 2.2 "private attribute?" warning. protected attr_reader :controller_name, :controller_file_name + private + def controller_class_path if options[:model_name] @controller_class_path diff --git a/railties/lib/rails/generators/test_unit/generator/generator_generator.rb b/railties/lib/rails/generators/test_unit/generator/generator_generator.rb index 59f8d40343..6b6e094453 100644 --- a/railties/lib/rails/generators/test_unit/generator/generator_generator.rb +++ b/railties/lib/rails/generators/test_unit/generator/generator_generator.rb @@ -12,7 +12,7 @@ module TestUnit # :nodoc: template "generator_test.rb", File.join("test/lib/generators", class_path, "#{file_name}_generator_test.rb") end - protected + private def generator_path if options[:namespace] diff --git a/railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb b/railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb index 806279788e..67bff8e4f9 100644 --- a/railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb +++ b/railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb @@ -17,7 +17,7 @@ module TestUnit # :nodoc: template "preview.rb", File.join("test/mailers/previews", class_path, "#{file_name}_mailer_preview.rb") end - protected + private def file_name @_file_name ||= super.gsub(/_mailer/i, "") end diff --git a/railties/lib/rails/generators/testing/behaviour.rb b/railties/lib/rails/generators/testing/behaviour.rb index a1e5a233b9..64d641d096 100644 --- a/railties/lib/rails/generators/testing/behaviour.rb +++ b/railties/lib/rails/generators/testing/behaviour.rb @@ -82,23 +82,23 @@ module Rails Rails::Generators::GeneratedAttribute.parse([name, attribute_type, index].compact.join(":")) end - protected + private - def destination_root_is_set? # :nodoc: + def destination_root_is_set? raise "You need to configure your Rails::Generators::TestCase destination root." unless destination_root end - def ensure_current_path # :nodoc: + def ensure_current_path cd current_path end # Clears all files and directories in destination. - def prepare_destination + def prepare_destination # :doc: rm_rf(destination_root) mkdir_p(destination_root) end - def migration_file_name(relative) # :nodoc: + def migration_file_name(relative) absolute = File.expand_path(relative, destination_root) dirname, file_name = File.dirname(absolute), File.basename(absolute).sub(/\.rb$/, "") Dir.glob("#{dirname}/[0-9]*_*.rb").grep(/\d+_#{file_name}.rb$/).first diff --git a/railties/lib/rails/mailers_controller.rb b/railties/lib/rails/mailers_controller.rb index 95de998208..000ce40fbc 100644 --- a/railties/lib/rails/mailers_controller.rb +++ b/railties/lib/rails/mailers_controller.rb @@ -40,12 +40,12 @@ class Rails::MailersController < Rails::ApplicationController # :nodoc: end end - protected - def show_previews? + private + def show_previews? # :doc: ActionMailer::Base.show_previews end - def find_preview + def find_preview # :doc: candidates = [] params[:path].to_s.scan(%r{/|$}) { candidates << $` } preview = candidates.detect { |candidate| ActionMailer::Preview.exists?(candidate) } @@ -57,7 +57,7 @@ class Rails::MailersController < Rails::ApplicationController # :nodoc: end end - def find_preferred_part(*formats) + def find_preferred_part(*formats) # :doc: formats.each do |format| if part = @email.find_first_mime_type(format) return part @@ -69,7 +69,7 @@ class Rails::MailersController < Rails::ApplicationController # :nodoc: end end - def find_part(format) + def find_part(format) # :doc: if part = @email.find_first_mime_type(format) part elsif @email.mime_type == format diff --git a/railties/lib/rails/rack/debugger.rb b/railties/lib/rails/rack/debugger.rb deleted file mode 100644 index dccfa3e9bb..0000000000 --- a/railties/lib/rails/rack/debugger.rb +++ /dev/null @@ -1,3 +0,0 @@ -require "active_support/deprecation" - -ActiveSupport::Deprecation.warn("This file is deprecated and will be removed in Rails 5.1 with no replacement.") diff --git a/railties/lib/rails/rack/logger.rb b/railties/lib/rails/rack/logger.rb index e3fee75603..853fc26051 100644 --- a/railties/lib/rails/rack/logger.rb +++ b/railties/lib/rails/rack/logger.rb @@ -27,45 +27,43 @@ module Rails end end - protected + private - def call_app(request, env) - instrumenter = ActiveSupport::Notifications.instrumenter - instrumenter.start "request.action_dispatch", request: request - logger.info { started_request_message(request) } - resp = @app.call(env) - resp[2] = ::Rack::BodyProxy.new(resp[2]) { finish(request) } - resp - rescue Exception - finish(request) - raise - ensure - ActiveSupport::LogSubscriber.flush_all! - end + def call_app(request, env) # :doc: + instrumenter = ActiveSupport::Notifications.instrumenter + instrumenter.start "request.action_dispatch", request: request + logger.info { started_request_message(request) } + resp = @app.call(env) + resp[2] = ::Rack::BodyProxy.new(resp[2]) { finish(request) } + resp + rescue Exception + finish(request) + raise + ensure + ActiveSupport::LogSubscriber.flush_all! + end - # Started GET "/session/new" for 127.0.0.1 at 2012-09-26 14:51:42 -0700 - def started_request_message(request) - 'Started %s "%s" for %s at %s' % [ - request.request_method, - request.filtered_path, - request.ip, - Time.now.to_default_s ] - end + # Started GET "/session/new" for 127.0.0.1 at 2012-09-26 14:51:42 -0700 + def started_request_message(request) # :doc: + 'Started %s "%s" for %s at %s' % [ + request.request_method, + request.filtered_path, + request.ip, + Time.now.to_default_s ] + end - def compute_tags(request) - @taggers.collect do |tag| - case tag - when Proc - tag.call(request) - when Symbol - request.send(tag) - else - tag + def compute_tags(request) # :doc: + @taggers.collect do |tag| + case tag + when Proc + tag.call(request) + when Symbol + request.send(tag) + else + tag + end end end - end - - private def finish(request) instrumenter = ActiveSupport::Notifications.instrumenter diff --git a/railties/lib/rails/railtie.rb b/railties/lib/rails/railtie.rb index 696db61f01..474118c0a9 100644 --- a/railties/lib/rails/railtie.rb +++ b/railties/lib/rails/railtie.rb @@ -173,8 +173,8 @@ module Rails instance.configure(&block) end - protected - def generate_railtie_name(string) #:nodoc: + private + def generate_railtie_name(string) ActiveSupport::Inflector.underscore(string).tr("/", "_") end @@ -188,7 +188,6 @@ module Rails end end - private # receives an instance variable identifier, set the variable value if is # blank and append given block to value, which will be used later in # `#each_registered_block(type, &block)` diff --git a/railties/lib/rails/railtie/configurable.rb b/railties/lib/rails/railtie/configurable.rb index 39f1f87575..2a8295426e 100644 --- a/railties/lib/rails/railtie/configurable.rb +++ b/railties/lib/rails/railtie/configurable.rb @@ -24,7 +24,7 @@ module Rails class_eval(&block) end - protected + private def method_missing(*args, &block) instance.send(*args, &block) diff --git a/railties/lib/rails/tasks/framework.rake b/railties/lib/rails/tasks/framework.rake index a2167796eb..d42d001b1a 100644 --- a/railties/lib/rails/tasks/framework.rake +++ b/railties/lib/rails/tasks/framework.rake @@ -73,15 +73,3 @@ namespace :app do end end end - -namespace :rails do - %i(update template templates:copy update:configs update:bin).each do |task_name| - task "#{task_name}" do - ActiveSupport::Deprecation.warn(<<-MSG.squish) - Running #{task_name} with the rails: namespace is deprecated in favor of app: namespace. - Run bin/rails app:#{task_name} instead. - MSG - Rake.application.invoke_task("app:#{task_name}") - end - end -end diff --git a/railties/lib/rails/tasks/log.rake b/railties/lib/rails/tasks/log.rake index c376234fee..ba796845d7 100644 --- a/railties/lib/rails/tasks/log.rake +++ b/railties/lib/rails/tasks/log.rake @@ -3,7 +3,7 @@ namespace :log do ## # Truncates all/specified log files # ENV['LOGS'] - # - defaults to standard environment log files i.e. 'development,test,production' + # - defaults to all environments log files i.e. 'development,test,production' # - ENV['LOGS']=all truncates all files i.e. log/*.log # - ENV['LOGS']='test,development' truncates only specified files desc "Truncates all/specified *.log files in log/ to zero bytes (specify which logs with LOGS=test,development)" @@ -19,7 +19,7 @@ namespace :log do elsif ENV["LOGS"] log_files_to_truncate(ENV["LOGS"]) else - log_files_to_truncate("development,test,production") + log_files_to_truncate(all_environments.join(",")) end end @@ -33,4 +33,8 @@ namespace :log do f = File.open(file, "w") f.close end + + def all_environments + Dir["config/environments/*.rb"].map { |fname| File.basename(fname, ".*") } + end end diff --git a/railties/lib/rails/tasks/routes.rake b/railties/lib/rails/tasks/routes.rake index f5e5b9ae87..04daaf4a83 100644 --- a/railties/lib/rails/tasks/routes.rake +++ b/railties/lib/rails/tasks/routes.rake @@ -7,15 +7,8 @@ task routes: :environment do all_routes = Rails.application.routes.routes require "action_dispatch/routing/inspector" inspector = ActionDispatch::Routing::RoutesInspector.new(all_routes) - if ARGV.any? { |argv| argv.start_with? "CONTROLLER" } - puts <<-eow.strip_heredoc - Passing `CONTROLLER` to `bin/rails routes` is deprecated and will be removed in Rails 5.1. - Please use `bin/rails routes -c controller_name` instead. - eow - end routes_filter = nil - routes_filter = { controller: ENV["CONTROLLER"] } if ENV["CONTROLLER"] OptionParser.new do |opts| opts.banner = "Usage: rails routes [options]" diff --git a/railties/lib/rails/tasks/statistics.rake b/railties/lib/rails/tasks/statistics.rake index 8265aef10b..ba1697186e 100644 --- a/railties/lib/rails/tasks/statistics.rake +++ b/railties/lib/rails/tasks/statistics.rake @@ -10,7 +10,6 @@ STATS_DIRECTORIES = [ %w(Channels app/channels), %w(JavaScripts app/assets/javascripts), %w(Libraries lib/), - %w(Tasks lib/tasks), %w(APIs app/apis), %w(Controller\ tests test/controllers), %w(Helper\ tests test/helpers), diff --git a/railties/lib/rails/test_help.rb b/railties/lib/rails/test_help.rb index db341dd847..5fda160012 100644 --- a/railties/lib/rails/test_help.rb +++ b/railties/lib/rails/test_help.rb @@ -17,7 +17,7 @@ if defined?(ActiveRecord::Base) class ActiveSupport::TestCase include ActiveRecord::TestFixtures self.fixture_path = "#{Rails.root}/test/fixtures/" - self.file_fixture_path = self.fixture_path + "files" + self.file_fixture_path = fixture_path + "files" end ActionDispatch::IntegrationTest.fixture_path = ActiveSupport::TestCase.fixture_path diff --git a/railties/test/application/assets_test.rb b/railties/test/application/assets_test.rb index edd43503bf..f38cacd6da 100644 --- a/railties/test/application/assets_test.rb +++ b/railties/test/application/assets_test.rb @@ -384,7 +384,7 @@ module ApplicationTests get "/assets/demo.js" assert_match "alert()", last_response.body - assert_equal nil, last_response.headers["Set-Cookie"] + assert_nil last_response.headers["Set-Cookie"] end test "files in any assets/ directories are not added to Sprockets" do diff --git a/railties/test/application/bin_setup_test.rb b/railties/test/application/bin_setup_test.rb index 0bbd25db2b..f62313f3e1 100644 --- a/railties/test/application/bin_setup_test.rb +++ b/railties/test/application/bin_setup_test.rb @@ -6,17 +6,10 @@ module ApplicationTests def setup build_app - - create_gemfile - update_boot_file_to_use_bundler - @old_gemfile_env = ENV["BUNDLE_GEMFILE"] - ENV["BUNDLE_GEMFILE"] = app_path + "/Gemfile" end def teardown teardown_app - - ENV["BUNDLE_GEMFILE"] = @old_gemfile_env end def test_bin_setup @@ -47,6 +40,7 @@ module ApplicationTests output = `bin/setup 2>&1` assert_equal(<<-OUTPUT, output) == Installing dependencies == +Resolving dependencies... The Gemfile's dependencies are satisfied == Preparing database == @@ -59,16 +53,5 @@ Created database 'db/test.sqlite3' OUTPUT end end - - private - def create_gemfile - app_file("Gemfile", "source 'https://rubygems.org'") - app_file("Gemfile", "gem 'rails', path: '#{RAILS_FRAMEWORK_ROOT}'", "a") - app_file("Gemfile", "gem 'sqlite3'", "a") - end - - def update_boot_file_to_use_bundler - app_file("config/boot.rb", "require 'bundler/setup'") - end end end diff --git a/railties/test/application/configuration/custom_test.rb b/railties/test/application/configuration/custom_test.rb index 13fc98f0d6..3c675eec71 100644 --- a/railties/test/application/configuration/custom_test.rb +++ b/railties/test/application/configuration/custom_test.rb @@ -28,7 +28,7 @@ module ApplicationTests assert_equal 3, x.payment_processing.retries assert_equal true, x.super_debugger assert_equal false, x.hyper_debugger - assert_equal nil, x.nil_debugger + assert_nil x.nil_debugger assert_nil x.i_do_not_exist.zomg end diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index c409f1ea79..01a9807b6b 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -78,6 +78,18 @@ module ApplicationTests end end + test "Rails.env falls back to development if RAILS_ENV is blank and RACK_ENV is nil" do + with_rails_env("") do + assert_equal "development", Rails.env + end + end + + test "Rails.env falls back to development if RACK_ENV is blank and RAILS_ENV is nil" do + with_rack_env("") do + assert_equal "development", Rails.env + end + end + test "By default logs tags are not set in development" do restore_default_config @@ -369,26 +381,6 @@ module ApplicationTests end end - test "config.serve_static_files is deprecated" do - make_basic_app do |application| - assert_deprecated do - application.config.serve_static_files = true - end - - assert application.config.public_file_server.enabled - end - end - - test "config.static_cache_control is deprecated" do - make_basic_app do |application| - assert_deprecated do - application.config.static_cache_control = "public, max-age=60" - end - - assert_equal application.config.static_cache_control, "public, max-age=60" - end - end - test "Use key_generator when secret_key_base is set" do make_basic_app do |application| application.secrets.secret_key_base = "b3c631c314c0bbca50c1b2843150fe33" @@ -614,7 +606,7 @@ module ApplicationTests app "development" assert_equal "b3c631c314c0bbca50c1b2843150fe33", app.config.secret_token - assert_equal nil, app.secrets.secret_key_base + assert_nil app.secrets.secret_key_base assert_equal app.key_generator.class, ActiveSupport::LegacyKeyGenerator end @@ -630,7 +622,7 @@ module ApplicationTests app "development" assert_equal "", app.config.secret_token - assert_equal nil, app.secrets.secret_key_base + assert_nil app.secrets.secret_key_base assert_raise ArgumentError, /\AA secret is required/ do app.key_generator end @@ -992,7 +984,7 @@ module ApplicationTests class ::OmgController < ActionController::Base def index - render plain: env["action_dispatch.show_exceptions"] + render plain: request.env["action_dispatch.show_exceptions"] end end @@ -1204,7 +1196,7 @@ module ApplicationTests application.config.session_store :disabled end - assert_equal nil, app.config.session_store + assert_nil app.config.session_store end test "default session store initializer sets session store to cookie store" do diff --git a/railties/test/application/generators_test.rb b/railties/test/application/generators_test.rb index 0153f94504..d2ce14f594 100644 --- a/railties/test/application/generators_test.rb +++ b/railties/test/application/generators_test.rb @@ -169,5 +169,20 @@ module ApplicationTests assert File.exist?(File.join(rails_root, "app/views/notifier_mailer/foo.text.erb")) assert File.exist?(File.join(rails_root, "app/views/notifier_mailer/foo.html.erb")) end + + test "ARGV is mutated as expected" do + require "#{app_path}/config/environment" + Rails::Command.const_set("APP_PATH", "rails/all") + + FileUtils.cd(rails_root) do + ARGV = ["mailer", "notifier", "foo"] + Rails::Command.const_set("ARGV", ARGV) + quietly { Rails::Command.invoke :generate, ARGV } + + assert_equal ["notifier", "foo"], ARGV + end + + Rails::Command.send(:remove_const, "APP_PATH") + end end end diff --git a/railties/test/application/initializers/frameworks_test.rb b/railties/test/application/initializers/frameworks_test.rb index 32bce7d372..90927159dd 100644 --- a/railties/test/application/initializers/frameworks_test.rb +++ b/railties/test/application/initializers/frameworks_test.rb @@ -219,7 +219,7 @@ module ApplicationTests end require "#{app_path}/config/environment" ActiveRecord::Base.connection.drop_table("posts") # force drop posts table for test. - assert ActiveRecord::Base.connection.schema_cache.tables("posts") + assert ActiveRecord::Base.connection.schema_cache.data_sources("posts") end test "expire schema cache dump" do @@ -227,10 +227,8 @@ module ApplicationTests `rails generate model post title:string; bin/rails db:migrate db:schema:cache:dump db:rollback` end - silence_warnings { - require "#{app_path}/config/environment" - assert !ActiveRecord::Base.connection.schema_cache.tables("posts") - } + require "#{app_path}/config/environment" + assert !ActiveRecord::Base.connection.schema_cache.data_sources("posts") end test "active record establish_connection uses Rails.env if DATABASE_URL is not set" do diff --git a/railties/test/application/loading_test.rb b/railties/test/application/loading_test.rb index 38f96f3ab9..c75a25bc6f 100644 --- a/railties/test/application/loading_test.rb +++ b/railties/test/application/loading_test.rb @@ -357,7 +357,7 @@ class LoadingTest < ActiveSupport::TestCase assert Rails.application.initialized? end - protected + private def setup_ar! ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:") diff --git a/railties/test/application/middleware/session_test.rb b/railties/test/application/middleware/session_test.rb index 0e4acfdcec..959a629ede 100644 --- a/railties/test/application/middleware/session_test.rb +++ b/railties/test/application/middleware/session_test.rb @@ -173,7 +173,7 @@ module ApplicationTests secret = app.key_generator.generate_key("encrypted cookie") sign_secret = app.key_generator.generate_key("signed encrypted cookie") - encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret) + encryptor = ActiveSupport::MessageEncryptor.new(secret[0, ActiveSupport::MessageEncryptor.key_len], sign_secret) get "/foo/read_raw_cookie" assert_equal 1, encryptor.decrypt_and_verify(last_response.body)["foo"] @@ -222,7 +222,7 @@ module ApplicationTests secret = app.key_generator.generate_key("encrypted cookie") sign_secret = app.key_generator.generate_key("signed encrypted cookie") - encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret) + encryptor = ActiveSupport::MessageEncryptor.new(secret[0, ActiveSupport::MessageEncryptor.key_len], sign_secret) get "/foo/read_raw_cookie" assert_equal 1, encryptor.decrypt_and_verify(last_response.body)["foo"] @@ -281,7 +281,7 @@ module ApplicationTests secret = app.key_generator.generate_key("encrypted cookie") sign_secret = app.key_generator.generate_key("signed encrypted cookie") - encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret) + encryptor = ActiveSupport::MessageEncryptor.new(secret[0, ActiveSupport::MessageEncryptor.key_len], sign_secret) get "/foo/read_raw_cookie" assert_equal 2, encryptor.decrypt_and_verify(last_response.body)["foo"] diff --git a/railties/test/application/middleware_test.rb b/railties/test/application/middleware_test.rb index 9baf7360a5..0a6e5b52e9 100644 --- a/railties/test/application/middleware_test.rb +++ b/railties/test/application/middleware_test.rb @@ -30,10 +30,10 @@ module ApplicationTests "Rack::Runtime", "Rack::MethodOverride", "ActionDispatch::RequestId", - "Rails::Rack::Logger", # must come after Rack::MethodOverride to properly log overridden methods + "ActionDispatch::RemoteIp", + "Rails::Rack::Logger", "ActionDispatch::ShowExceptions", "ActionDispatch::DebugExceptions", - "ActionDispatch::RemoteIp", "ActionDispatch::Reloader", "ActionDispatch::Callbacks", "ActiveRecord::Migration::CheckPending", @@ -58,10 +58,10 @@ module ApplicationTests "ActiveSupport::Cache::Strategy::LocalCache", "Rack::Runtime", "ActionDispatch::RequestId", - "Rails::Rack::Logger", # must come after Rack::MethodOverride to properly log overridden methods + "ActionDispatch::RemoteIp", + "Rails::Rack::Logger", "ActionDispatch::ShowExceptions", "ActionDispatch::DebugExceptions", - "ActionDispatch::RemoteIp", "ActionDispatch::Reloader", "ActionDispatch::Callbacks", "Rack::Head", @@ -70,6 +70,37 @@ module ApplicationTests ], middleware end + test "middleware dependencies" do + boot! + + # The following array-of-arrays describes dependencies between + # middlewares: the first item in each list depends on the + # remaining items (and therefore must occur later in the + # middleware stack). + + dependencies = [ + # Logger needs a fully "corrected" request environment + %w(Rails::Rack::Logger Rack::MethodOverride ActionDispatch::RequestId ActionDispatch::RemoteIp), + + # Serving public/ doesn't invoke user code, so it should skip + # locks etc + %w(ActionDispatch::Executor ActionDispatch::Static), + + # Errors during reload must be reported + %w(ActionDispatch::Reloader ActionDispatch::ShowExceptions ActionDispatch::DebugExceptions), + + # Outright dependencies + %w(ActionDispatch::Static Rack::Sendfile), + %w(ActionDispatch::Flash ActionDispatch::Session::CookieStore), + %w(ActionDispatch::Session::CookieStore ActionDispatch::Cookies), + ] + + require "tsort" + sorted = TSort.tsort((middleware | dependencies.flatten).method(:each), + lambda { |n, &b| dependencies.each { |m, *ds| ds.each(&b) if m == n } }) + assert_equal sorted, middleware + end + test "Rack::Cache is not included by default" do boot! @@ -246,7 +277,7 @@ module ApplicationTests get "/", {}, "HTTP_IF_NONE_MATCH" => etag assert_equal 304, last_response.status assert_equal "", last_response.body - assert_equal nil, last_response.headers["Content-Type"] + assert_nil last_response.headers["Content-Type"] assert_equal "max-age=0, private, must-revalidate", last_response.headers["Cache-Control"] assert_equal etag, last_response.headers["Etag"] @@ -255,7 +286,7 @@ module ApplicationTests assert_equal "", last_response.body assert_equal "text/plain; charset=utf-8", last_response.headers["Content-Type"] assert_equal "no-cache", last_response.headers["Cache-Control"] - assert_equal nil, last_response.headers["Etag"] + assert_nil last_response.headers["Etag"] end test "ORIGINAL_FULLPATH is passed to env" do diff --git a/railties/test/application/rake/log_test.rb b/railties/test/application/rake/log_test.rb new file mode 100644 index 0000000000..fdd3c71fe8 --- /dev/null +++ b/railties/test/application/rake/log_test.rb @@ -0,0 +1,33 @@ +require "isolation/abstract_unit" + +module ApplicationTests + module RakeTests + class LogTest < ActiveSupport::TestCase + include ActiveSupport::Testing::Isolation + + def setup + build_app + end + + def teardown + teardown_app + end + + test "log:clear clear all environments log files by default" do + Dir.chdir(app_path) do + File.open("config/environments/staging.rb", "w") + + File.write("log/staging.log", "staging") + File.write("log/test.log", "test") + File.write("log/dummy.log", "dummy") + + `rails log:clear` + + assert_equal 0, File.size("log/test.log") + assert_equal 0, File.size("log/staging.log") + assert_equal 5, File.size("log/dummy.log") + end + end + end + end +end diff --git a/railties/test/application/rake_test.rb b/railties/test/application/rake_test.rb index d80a45a83f..1b64a0a1ca 100644 --- a/railties/test/application/rake_test.rb +++ b/railties/test/application/rake_test.rb @@ -132,48 +132,6 @@ module ApplicationTests assert_equal "Prefix Verb URI Pattern Controller#Action\n cart GET /cart(.:format) cart#show\n", output end - def test_rails_routes_with_controller_environment - app_file "config/routes.rb", <<-RUBY - Rails.application.routes.draw do - get '/cart', to: 'cart#show' - get '/basketball', to: 'basketball#index' - end - RUBY - - output = Dir.chdir(app_path) { `bin/rails routes CONTROLLER=cart` } - assert_equal ["Passing `CONTROLLER` to `bin/rails routes` is deprecated and will be removed in Rails 5.1.", - "Please use `bin/rails routes -c controller_name` instead.", - "Prefix Verb URI Pattern Controller#Action", - " cart GET /cart(.:format) cart#show\n"].join("\n"), output - - output = Dir.chdir(app_path) { `bin/rails routes -c cart` } - assert_equal "Prefix Verb URI Pattern Controller#Action\n cart GET /cart(.:format) cart#show\n", output - end - - def test_rails_routes_with_namespaced_controller_environment - app_file "config/routes.rb", <<-RUBY - Rails.application.routes.draw do - namespace :admin do - resource :post - end - end - RUBY - expected_output = [" Prefix Verb URI Pattern Controller#Action", - " new_admin_post GET /admin/post/new(.:format) admin/posts#new", - "edit_admin_post GET /admin/post/edit(.:format) admin/posts#edit", - " admin_post GET /admin/post(.:format) admin/posts#show", - " PATCH /admin/post(.:format) admin/posts#update", - " PUT /admin/post(.:format) admin/posts#update", - " DELETE /admin/post(.:format) admin/posts#destroy", - " POST /admin/post(.:format) admin/posts#create\n"].join("\n") - - output = Dir.chdir(app_path) { `bin/rails routes -c Admin::PostController` } - assert_equal expected_output, output - - output = Dir.chdir(app_path) { `bin/rails routes -c PostController` } - assert_equal expected_output, output - end - def test_singular_resource_output_in_rake_routes app_file "config/routes.rb", <<-RUBY Rails.application.routes.draw do @@ -232,6 +190,30 @@ module ApplicationTests assert_equal "Prefix Verb URI Pattern Controller#Action\n cart GET /cart(.:format) cart#show\n", output end + def test_rails_routes_with_namespaced_controller_search_key + app_file "config/routes.rb", <<-RUBY + Rails.application.routes.draw do + namespace :admin do + resource :post + end + end + RUBY + expected_output = [" Prefix Verb URI Pattern Controller#Action", + " new_admin_post GET /admin/post/new(.:format) admin/posts#new", + "edit_admin_post GET /admin/post/edit(.:format) admin/posts#edit", + " admin_post GET /admin/post(.:format) admin/posts#show", + " PATCH /admin/post(.:format) admin/posts#update", + " PUT /admin/post(.:format) admin/posts#update", + " DELETE /admin/post(.:format) admin/posts#destroy", + " POST /admin/post(.:format) admin/posts#create\n"].join("\n") + + output = Dir.chdir(app_path) { `bin/rails routes -c Admin::PostController` } + assert_equal expected_output, output + + output = Dir.chdir(app_path) { `bin/rails routes -c PostController` } + assert_equal expected_output, output + end + def test_rails_routes_displays_message_when_no_routes_are_defined app_file "config/routes.rb", <<-RUBY Rails.application.routes.draw do @@ -341,16 +323,6 @@ module ApplicationTests assert_no_match(/Errors running/, output) end - def test_db_test_clone_when_using_sql_format - add_to_config "config.active_record.schema_format = :sql" - output = Dir.chdir(app_path) do - `bin/rails generate scaffold user username:string; - bin/rails db:migrate; - bin/rails db:test:clone 2>&1 --trace` - end - assert_match(/Execute db:test:clone_structure/, output) - end - def test_db_test_prepare_when_using_sql_format add_to_config "config.active_record.schema_format = :sql" output = Dir.chdir(app_path) do diff --git a/railties/test/commands/server_test.rb b/railties/test/commands/server_test.rb index 391886bf33..527529aa52 100644 --- a/railties/test/commands/server_test.rb +++ b/railties/test/commands/server_test.rb @@ -7,31 +7,35 @@ class Rails::ServerTest < ActiveSupport::TestCase include EnvHelpers def test_environment_with_server_option - args = ["thin", "-e", "production"] - options = Rails::Server::Options.new.parse!(args) + args = ["thin", "-e", "production"] + options = parse_arguments(args) assert_equal "production", options[:environment] assert_equal "thin", options[:server] end def test_environment_without_server_option - args = ["-e", "production"] - options = Rails::Server::Options.new.parse!(args) + args = ["-e", "production"] + options = parse_arguments(args) assert_equal "production", options[:environment] assert_nil options[:server] end def test_server_option_without_environment - args = ["thin"] - options = Rails::Server::Options.new.parse!(args) - assert_nil options[:environment] - assert_equal "thin", options[:server] + args = ["thin"] + with_rack_env nil do + with_rails_env nil do + options = parse_arguments(args) + assert_equal "development", options[:environment] + assert_equal "thin", options[:server] + end + end end def test_environment_with_rails_env with_rack_env nil do with_rails_env "production" do - server = Rails::Server.new - assert_equal "production", server.options[:environment] + options = parse_arguments + assert_equal "production", options[:environment] end end end @@ -39,40 +43,39 @@ class Rails::ServerTest < ActiveSupport::TestCase def test_environment_with_rack_env with_rails_env nil do with_rack_env "production" do - server = Rails::Server.new - assert_equal "production", server.options[:environment] + options = parse_arguments + assert_equal "production", options[:environment] end end end def test_environment_with_port switch_env "PORT", "1234" do - server = Rails::Server.new - assert_equal 1234, server.options[:Port] + options = parse_arguments + assert_equal 1234, options[:Port] end end def test_environment_with_host switch_env "HOST", "1.2.3.4" do - server = Rails::Server.new - assert_equal "1.2.3.4", server.options[:Host] + options = parse_arguments + assert_equal "1.2.3.4", options[:Host] end end def test_caching_without_option args = [] - options = Rails::Server::Options.new.parse!(args) - merged_options = Rails::Server.new.default_options.merge(options) - assert_equal nil, merged_options[:caching] + options = parse_arguments(args) + assert_nil options[:caching] end def test_caching_with_option args = ["--dev-caching"] - options = Rails::Server::Options.new.parse!(args) + options = parse_arguments(args) assert_equal true, options[:caching] args = ["--no-dev-caching"] - options = Rails::Server::Options.new.parse!(args) + options = parse_arguments(args) assert_equal false, options[:caching] end @@ -80,38 +83,38 @@ class Rails::ServerTest < ActiveSupport::TestCase with_rack_env nil do with_rails_env nil do args = [] - options = Rails::Server::Options.new.parse!(args) + options = parse_arguments(args) assert_equal true, options[:log_stdout] args = ["-e", "development"] - options = Rails::Server::Options.new.parse!(args) + options = parse_arguments(args) assert_equal true, options[:log_stdout] args = ["-e", "production"] - options = Rails::Server::Options.new.parse!(args) + options = parse_arguments(args) assert_equal false, options[:log_stdout] with_rack_env "development" do args = [] - options = Rails::Server::Options.new.parse!(args) + options = parse_arguments(args) assert_equal true, options[:log_stdout] end with_rack_env "production" do args = [] - options = Rails::Server::Options.new.parse!(args) + options = parse_arguments(args) assert_equal false, options[:log_stdout] end with_rails_env "development" do args = [] - options = Rails::Server::Options.new.parse!(args) + options = parse_arguments(args) assert_equal true, options[:log_stdout] end with_rails_env "production" do args = [] - options = Rails::Server::Options.new.parse!(args) + options = parse_arguments(args) assert_equal false, options[:log_stdout] end end @@ -119,25 +122,29 @@ class Rails::ServerTest < ActiveSupport::TestCase end def test_default_options - server = Rails::Server.new - old_default_options = server.default_options + old_default_options = parse_arguments Dir.chdir("..") do - assert_equal old_default_options, server.default_options + default_options = parse_arguments + assert_equal old_default_options, default_options end end def test_restart_command_contains_customized_options original_args = ARGV.dup - args = ["-p", "4567"] + args = %w(-p 4567 -b 127.0.0.1 -c dummy_config.ru -d -e test -P tmp/server.pid -C) ARGV.replace args - options = Rails::Server::Options.new.parse! args - server = Rails::Server.new options - expected = "bin/rails server -p 4567" + options = parse_arguments(args) + expected = "bin/rails server -p 4567 -b 127.0.0.1 -c dummy_config.ru -d -e test -P tmp/server.pid -C" - assert_equal expected, server.default_options[:restart_cmd] + assert_equal expected, options[:restart_cmd] ensure ARGV.replace original_args end + + private + def parse_arguments(args = []) + Rails::Command::ServerCommand.new([], args).server_options + end end diff --git a/railties/test/generators/actions_test.rb b/railties/test/generators/actions_test.rb index 0a26897a4d..360e8e97d7 100644 --- a/railties/test/generators/actions_test.rb +++ b/railties/test/generators/actions_test.rb @@ -369,7 +369,7 @@ F assert_equal("", action(:log, :yes, "YES")) end - protected + private def action(*args, &block) capture(:stdout) { generator.send(*args, &block) } diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index 9285a9f091..20de2258c5 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -419,6 +419,13 @@ class AppGeneratorTest < Rails::Generators::TestCase end end + def test_generator_if_skip_yarn_is_given + run_generator [destination_root, "--skip-yarn"] + + assert_no_file "vendor/package.json" + assert_no_file "bin/yarn" + end + def test_generator_if_skip_action_cable_is_given run_generator [destination_root, "--skip-action-cable"] assert_file "config/application.rb", /#\s+require\s+["']action_cable\/engine["']/ @@ -788,7 +795,7 @@ class AppGeneratorTest < Rails::Generators::TestCase assert_equal 3, @sequence_step end - protected + private def stub_rails_application(root) Rails.application.config.root = root diff --git a/railties/test/generators/controller_generator_test.rb b/railties/test/generators/controller_generator_test.rb index 9b986a636a..af86a0136f 100644 --- a/railties/test/generators/controller_generator_test.rb +++ b/railties/test/generators/controller_generator_test.rb @@ -65,7 +65,7 @@ class ControllerGeneratorTest < Rails::Generators::TestCase def test_add_routes run_generator - assert_file "config/routes.rb", /get 'account\/foo'/, /get 'account\/bar'/ + assert_file "config/routes.rb", /^ get 'account\/foo'/, /^ get 'account\/bar'/ end def test_skip_routes diff --git a/railties/test/generators/named_base_test.rb b/railties/test/generators/named_base_test.rb index 0258b3b9d7..3015b5363b 100644 --- a/railties/test/generators/named_base_test.rb +++ b/railties/test/generators/named_base_test.rb @@ -131,7 +131,7 @@ class NamedBaseTest < Rails::Generators::TestCase assert_name g, "admin.foos", :controller_i18n_scope end - protected + private def assert_name(generator, value, method) assert_equal value, generator.send(method) diff --git a/railties/test/generators/plugin_generator_test.rb b/railties/test/generators/plugin_generator_test.rb index a0018dc782..9921a80342 100644 --- a/railties/test/generators/plugin_generator_test.rb +++ b/railties/test/generators/plugin_generator_test.rb @@ -714,7 +714,7 @@ class PluginGeneratorTest < Rails::Generators::TestCase end end - protected + private def action(*args, &block) silence(:stdout) { generator.send(*args, &block) } diff --git a/railties/test/generators/scaffold_controller_generator_test.rb b/railties/test/generators/scaffold_controller_generator_test.rb index bd23faf268..9971626f9f 100644 --- a/railties/test/generators/scaffold_controller_generator_test.rb +++ b/railties/test/generators/scaffold_controller_generator_test.rb @@ -230,6 +230,12 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase assert_match(/@user\.destroy/, m) end end + + assert_no_file "app/views/users/index.html.erb" + assert_no_file "app/views/users/edit.html.erb" + assert_no_file "app/views/users/show.html.erb" + assert_no_file "app/views/users/new.html.erb" + assert_no_file "app/views/users/_form.html.erb" end def test_api_controller_tests diff --git a/railties/test/rails_info_test.rb b/railties/test/rails_info_test.rb index 59e79de41a..9f4c5bb025 100644 --- a/railties/test/rails_info_test.rb +++ b/railties/test/rails_info_test.rb @@ -54,7 +54,7 @@ class InfoTest < ActiveSupport::TestCase end end - protected + private def properties Rails::Info.properties end diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb index 70a6cd90b2..52d691b73b 100644 --- a/railties/test/railties/engine_test.rb +++ b/railties/test/railties/engine_test.rb @@ -890,7 +890,7 @@ YAML boot_rails - assert_equal AppTemplate.railtie_namespace, AppTemplate::Engine + assert_equal AppTemplate::Engine, AppTemplate.railtie_namespace end test "properly reload routes" do diff --git a/tools/test.rb b/tools/test.rb index 824ee57c96..ce546b382d 100644 --- a/tools/test.rb +++ b/tools/test.rb @@ -4,6 +4,8 @@ require "bundler" Bundler.setup require "rails/test_unit/minitest_plugin" +require "rails/test_unit/line_filtering" +require "active_support/test_case" module Rails # Necessary to get rerun-snippts working. @@ -12,6 +14,7 @@ module Rails end end +ActiveSupport::TestCase.extend Rails::LineFiltering Rails::TestUnitReporter.executable = "bin/test" Minitest.run_via[:rails] = true require "active_support/testing/autorun" |